├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── COPYING ├── ChangeLog ├── README ├── SConstruct ├── clients ├── c │ ├── bridge.c │ ├── bridge.h │ ├── common.c │ ├── common.h │ ├── connection.c │ ├── connection.h │ ├── connection_info.h │ ├── main.c │ ├── message.h │ └── size.h └── java │ └── src │ └── com │ └── example │ ├── conf │ ├── Config.java │ └── Size.java │ ├── msg │ ├── Ack.java │ ├── Header.java │ ├── MsgDefine.java │ ├── MsgHandshake.java │ ├── MsgHelper.java │ ├── MsgReq.java │ ├── MsgRes.java │ └── MsgSegment.java │ ├── network │ ├── Connection.java │ ├── ConnectionDelegate.java │ └── Tester.java │ └── tool │ ├── Log.java │ └── NetworkDataHelper.java ├── gMaxLinked.conf ├── gMaxLinked.sh ├── gMaxLinkedd ├── log4crc └── src ├── common ├── BaseType.h ├── CAutoLock.cpp ├── CAutoLock.h ├── CAutoPtr.h ├── CBase.cpp ├── CBase.h ├── CCond.cpp ├── CCond.h ├── CConfReader.cpp ├── CConfReader.h ├── CLock.h ├── CLoopBuffer.cpp ├── CLoopBuffer.h ├── CMutex.cpp ├── CMutex.h ├── CResource.h ├── CSem.cpp ├── CSem.h ├── CTimerManager.cpp ├── CTimerManager.h ├── CWorker.cpp ├── CWorker.h ├── IWorkable.h └── SConscript ├── config ├── Config.cpp ├── Config.h ├── DefaultConfig.h ├── Length.h ├── SConscript └── Size.h ├── database ├── CRedisOperator.cpp ├── CRedisOperator.h ├── RedisStructure.md └── SConscript ├── gMaxLinked.cpp ├── traffic ├── CNode.cpp ├── CNode.h ├── CNodeGroup.cpp ├── CNodeGroup.h ├── CTrafficManager.cpp ├── CTrafficManager.h ├── Message.h └── SConscript └── transaction ├── CTransaction.cpp ├── CTransaction.h ├── CTransactionManager.cpp ├── CTransactionManager.h └── SConscript /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.out 3 | *.o 4 | *.a 5 | .sconsign.dblite 6 | .autotools 7 | .cproject 8 | .project 9 | .settings/ 10 | */.deps/ 11 | gMaxLinked -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) Launch", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/gMaxLinked", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": false, 17 | "MIMode": "gdb", 18 | "setupCommands": [ 19 | { 20 | "description": "Enable pretty-printing for gdb", 21 | "text": "-enable-pretty-printing", 22 | "ignoreFailures": true 23 | } 24 | ] 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Scons Build", 8 | "type": "shell", 9 | "command": "scons", 10 | "group": "build", 11 | "problemMatcher": [ 12 | "$gcc" 13 | ] 14 | }, 15 | { 16 | "label": "Scons Clean", 17 | "type": "shell", 18 | "command": "scons -c", 19 | "problemMatcher": [ 20 | "$gcc" 21 | ] 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | #1.0.0, 2016-09-07 2 | 1. The project construct tool is changed from autotools to SCons 3 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | gMaxLinked,a framework or a backend application for large concurrency, real-time, long-live tcp applications. 2 | 3 | You can use gMaxLinked to establish application used by android push, WeChat/Line backend server, etc. You can find the source code in src folder. 4 | 5 | There are C and Java code for corresponding clients reference, you can find them in client folder. You can use C client code directly, they have been tested with gMaxLinked as a server. The java code can be used as reference only due to not be up to date. 6 | 7 | gMaxLinked only work on linux 64-bit(I compile and run the application on CentOS 7 64-bit and Ubuntu 16.04 LTS 64-bit). 8 | 9 | Following libs is necessary for gMaxLinked: 10 | > pthread 11 | > hiredis(Ubuntu: apt install libhiredis-dev) 12 | > log4c(Ubuntu: apt install liblog4c-dev) 13 | 14 | Plase use SCons(Ubuntu: apt install scons) to build the project. 15 | 16 | Code Style: { BasedOnStyle: Google, UseTab: Never, IndentWidth: 4, TabWidth: 4, AccessModifierOffset: -4, NamespaceIndentation: All } -------------------------------------------------------------------------------- /SConstruct: -------------------------------------------------------------------------------- 1 | env = Environment() 2 | env.Append(CCFLAGS = '-std=c++11') 3 | 4 | sub_dirs = ['common', 'config', 'database', 'traffic', 'transaction'] 5 | sub_scripts = [] 6 | 7 | for dir in sub_dirs: 8 | sub_scripts += ['src/' + dir + '/SConscript'] 9 | 10 | sub_objs = SConscript(sub_scripts) 11 | env.Program('gMaxLinked', list(sub_objs) + ['src/gMaxLinked.cpp'], LIBS=['pthread', 'hiredis', 'log4c']) -------------------------------------------------------------------------------- /clients/c/bridge.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "connection_info.h" 6 | #include "message.h" 7 | #include "connection.h" 8 | #include "bridge.h" 9 | 10 | static struct connection_info _ci; 11 | static bool _started = false; 12 | static unsigned int _build = 0; 13 | static char _session_id[SIZE_SESSION_ID]; 14 | 15 | static struct bridge_callback_funs { 16 | ON_CONNECTED on_connected; 17 | ON_READY on_ready; 18 | ON_PUSH on_push; 19 | ON_SEND_ACK on_send_ack; 20 | ON_ERROR on_error; 21 | ON_CLOSED on_closed; 22 | } _bridge_callbacks; 23 | 24 | static void _on_connected(void *parameter) { 25 | printf("on_connected\n"); 26 | 27 | unsigned long long lastUpdate = 0; 28 | 29 | if (_bridge_callbacks.on_connected) { 30 | lastUpdate = _bridge_callbacks.on_connected(); 31 | } 32 | 33 | struct TPDUHandShake msg; 34 | time_t t; 35 | 36 | msg.header.size = sizeof(struct TPDUHandShake); 37 | msg.header.type = MT_ACCOUNT; 38 | msg.header.cmd = MC_HAND_SHAKE; 39 | msg.header.ver = 0x0100; 40 | msg.header.lang = ML_CN; 41 | msg.header.seq = 0; 42 | time(&t); 43 | msg.header.stmp = t; 44 | msg.header.ext = 0; 45 | 46 | msg.build = _build; 47 | msg.lastUpdate = lastUpdate; 48 | strncpy(msg.sessionId, _session_id, SIZE_SESSION_ID - 1); 49 | msg.sessionId[16] = 0; 50 | 51 | if (0 != cnn_send(&_ci, (unsigned char *) &msg, 52 | sizeof(struct TPDUHandShake))) { 53 | printf("failed to send handshake msg\n"); 54 | 55 | return; 56 | } 57 | } 58 | 59 | static void _on_received(void *parameter, const unsigned char *data, 60 | unsigned int size) { 61 | printf("on_received: %d bytes\n", size); 62 | 63 | struct THeader *header = (struct THeader *) data; 64 | 65 | if ((MT_ACCOUNT | MT_SIGN_ACK) == header->type 66 | && MC_HAND_SHAKE == header->cmd 67 | && sizeof(struct TPDUHandShakeAck) == size) { 68 | struct TPDUHandShakeAck *msg = (struct TPDUHandShakeAck *) data; 69 | printf("on_received: get TPDUHandShakeAck\n"); 70 | 71 | if (msg->ack.code) { 72 | printf("on_received: failed to handshake with code-%d\n", 73 | msg->ack.code); 74 | } else if (_bridge_callbacks.on_ready) { 75 | _bridge_callbacks.on_ready(); 76 | } 77 | } else if (MT_CONTROL == header->type 78 | && MC_HEART_BEAT == header->cmd 79 | && sizeof(struct TPDUHeartBeat) == size) { 80 | printf("on_received: get TPDUHeartBeat\n"); 81 | struct TPDUHeartBeatAck msg; 82 | time_t t; 83 | 84 | memcpy(&msg, data, sizeof(struct TPDUHeartBeat)); 85 | msg.header.size = sizeof(struct TPDUHeartBeatAck); 86 | msg.header.type |= MT_SIGN_ACK; 87 | time(&t); 88 | msg.header.stmp = t; 89 | msg.ack.code = 0; 90 | 91 | if (0 != cnn_send(&_ci, (unsigned char *) &msg, 92 | sizeof(struct TPDUHeartBeatAck))) { 93 | printf("failed to send heartbeat ack msg\n"); 94 | } 95 | } else if (MT_SERVICE == header->type 96 | && MC_PUSH_MSG == header->cmd 97 | && sizeof(struct TPDUPushMsg) == size) { 98 | struct TPDUPushMsg *msg = (struct TPDUPushMsg *) data; 99 | printf("on_received: get TPDUPushMsg\n"); 100 | 101 | if (_bridge_callbacks.on_push) { 102 | _bridge_callbacks.on_push(msg->ornType, msg->ornId, msg->ornExtId, 103 | msg->messageId, msg->json, msg->header.stmp); 104 | } 105 | 106 | struct TPDUPushMsgAck msgAck; 107 | time_t t; 108 | 109 | memcpy(&msgAck.header, &msg->header, sizeof(struct THeader)); 110 | msgAck.header.size = sizeof(struct TPDUPushMsgAck); 111 | msgAck.header.type |= MT_SIGN_ACK; 112 | time(&t); 113 | msgAck.header.stmp = t; 114 | msgAck.ack.code = 0; 115 | 116 | if (0 != cnn_send(&_ci, (unsigned char *) &msgAck, 117 | sizeof(struct TPDUPushMsgAck))) { 118 | printf("failed to send push ack msg\n"); 119 | } 120 | } else if ((MT_SERVICE | MT_SIGN_ACK) == header->type 121 | && MC_SEND_MSG == header->cmd 122 | && sizeof(struct TPDUSendMsgAck) == size) { 123 | struct TPDUSendMsgAck *msg = (struct TPDUSendMsgAck *) data; 124 | printf("on_received: get TPDUSendMsgAck\n"); 125 | 126 | if (msg->ack.code) { 127 | printf("on_received: failed to send msg with code-%d\n", 128 | msg->ack.code); 129 | } else { 130 | if (_bridge_callbacks.on_send_ack) { 131 | _bridge_callbacks.on_send_ack(msg->header.seq, msg->ack.code, 132 | msg->messageId); 133 | } 134 | } 135 | } else { 136 | printf("on_received: get unknown messages\n"); 137 | } 138 | } 139 | 140 | static void _on_error(void *parameter, int err_code, const char *err_desc) { 141 | printf("on_error: %d-%s\n", err_code, err_desc); 142 | 143 | if (_bridge_callbacks.on_error) { 144 | _bridge_callbacks.on_error(err_code, err_desc); 145 | } 146 | } 147 | 148 | static void _on_closed(void *parameter) { 149 | printf("on_closed\n"); 150 | 151 | if (_bridge_callbacks.on_closed) { 152 | _bridge_callbacks.on_closed(); 153 | } 154 | } 155 | 156 | int net_worker_start(const char *ip, unsigned short port, 157 | unsigned int reconnect_interval, unsigned int build, 158 | const char *session_id) { 159 | if (true == _started) { 160 | printf("the proccess has been started"); 161 | 162 | return -1; 163 | } 164 | 165 | _started = true; 166 | 167 | memset(&_ci, 0, sizeof(struct connection_info)); 168 | strncpy(_ci.ip, ip, strlen(ip)); 169 | _ci.port = port; 170 | _ci.reconnect_interval = reconnect_interval; 171 | _ci.callback_funs.on_connected = _on_connected; 172 | _ci.callback_funs.on_received = _on_received; 173 | _ci.callback_funs.on_error = _on_error; 174 | _ci.callback_funs.on_closed = _on_closed; 175 | _build = build; 176 | memset(_session_id, 0, SIZE_SESSION_ID); 177 | strncpy(_session_id, session_id, SIZE_SESSION_ID - 1); 178 | 179 | return cnn_start(&_ci, true); 180 | } 181 | 182 | void net_worker_stop() { 183 | if (true == _started) { 184 | cnn_stop(&_ci, true); 185 | _started = false; 186 | } 187 | } 188 | 189 | int net_worker_send(unsigned char dstType, unsigned long long dstId, 190 | const char *json) { 191 | if (!json || 0 == json[0] || SIZE_JSON <= strlen(json)) { 192 | return 1; 193 | } 194 | 195 | if (1 != dstType && 2 != dstType) { 196 | return 2; 197 | } 198 | 199 | struct TPDUSendMsg msg; 200 | time_t t; 201 | 202 | memset(&msg, 0, sizeof(struct TPDUSendMsg)); 203 | msg.header.size = sizeof(struct TPDUSendMsg); 204 | msg.header.type = MT_SERVICE; 205 | msg.header.cmd = MC_SEND_MSG; 206 | msg.header.ver = 0x0100; 207 | msg.header.lang = ML_CN; 208 | msg.header.seq = 0; 209 | time(&t); 210 | msg.header.stmp = t; 211 | msg.header.ext = 0; 212 | 213 | msg.dstType = dstType; 214 | msg.dstId = dstId; 215 | strncpy(msg.json, json, SIZE_JSON - 1); 216 | 217 | return cnn_send(&_ci, (unsigned char *) &msg, sizeof(struct TPDUSendMsg)); 218 | } 219 | 220 | void set_on_connected(ON_CONNECTED on_connected) { 221 | _bridge_callbacks.on_connected = on_connected; 222 | } 223 | 224 | void set_on_ready(ON_READY on_ready) { 225 | _bridge_callbacks.on_ready = on_ready; 226 | } 227 | 228 | void set_on_push(ON_PUSH on_push) { 229 | _bridge_callbacks.on_push = on_push; 230 | } 231 | 232 | void set_on_send_ack(ON_SEND_ACK on_send_ack) { 233 | _bridge_callbacks.on_send_ack = on_send_ack; 234 | } 235 | 236 | void set_on_error(ON_ERROR on_error) { 237 | _bridge_callbacks.on_error = on_error; 238 | } 239 | 240 | void set_on_closed(ON_CLOSED on_closed) { 241 | _bridge_callbacks.on_closed = on_closed; 242 | } 243 | -------------------------------------------------------------------------------- /clients/c/bridge.h: -------------------------------------------------------------------------------- 1 | #ifndef _bridge_h 2 | #define _bridge_h 3 | 4 | int net_worker_start(const char *ip, unsigned short port, 5 | unsigned int reconnect_interval, unsigned int build, 6 | const char *session_id); 7 | 8 | void net_worker_stop(); 9 | 10 | int net_worker_send(unsigned char dstType, unsigned long long dstId, 11 | const char *json); 12 | 13 | typedef unsigned long long (*ON_CONNECTED)(); 14 | 15 | typedef void (*ON_READY)(); 16 | 17 | typedef void (*ON_PUSH)(unsigned char ornType, unsigned long long ornId, 18 | unsigned long long ornExtId, unsigned long long messageId, const char *json, 19 | unsigned long long stmp); 20 | 21 | typedef void (*ON_SEND_ACK)(unsigned long long seq, unsigned short code, 22 | unsigned long long messageId); 23 | 24 | typedef void (*ON_CLOSED)(); 25 | 26 | typedef void (*ON_ERROR)(int err_code, const char *err_desc); 27 | 28 | void set_on_connected(ON_CONNECTED on_connect); 29 | 30 | void set_on_ready(ON_READY on_ready); 31 | 32 | void set_on_push(ON_PUSH on_push); 33 | 34 | void set_on_send_ack(ON_SEND_ACK on_send_ack); 35 | 36 | void set_on_error(ON_ERROR on_error); 37 | 38 | void set_on_closed(ON_CLOSED on_closed); 39 | 40 | #endif /* _bridge_h */ 41 | -------------------------------------------------------------------------------- /clients/c/common.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | int goo_sleep(unsigned int s, unsigned int ms) { 8 | struct timeval delay; 9 | 10 | delay.tv_sec = s; 11 | delay.tv_usec = ms * 1000; 12 | 13 | return select(0, NULL, NULL, NULL, &delay); 14 | } 15 | -------------------------------------------------------------------------------- /clients/c/common.h: -------------------------------------------------------------------------------- 1 | #ifndef _common_h_ 2 | #define _common_h_ 3 | 4 | int goo_sleep(unsigned int s, unsigned int ms); 5 | 6 | #endif // _common_h_ 7 | -------------------------------------------------------------------------------- /clients/c/connection.c: -------------------------------------------------------------------------------- 1 | #include "connection.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "common.h" 17 | #include "connection_info.h" 18 | #include "message.h" 19 | 20 | static pthread_mutex_t _lock = PTHREAD_MUTEX_INITIALIZER; 21 | static unsigned int _connection_num; 22 | 23 | #define __lock pthread_mutex_lock(&_lock) 24 | #define __unlock pthread_mutex_unlock(&_lock) 25 | 26 | static void _set_socket_nonblocking(int socket) { 27 | int opts = fcntl(socket, F_GETFL); 28 | int opt = 1; 29 | 30 | if (0 > opts) { 31 | printf("failed to call fcntl with F_GETFL\n"); 32 | exit(1); 33 | } 34 | 35 | opts |= O_NONBLOCK; 36 | 37 | if (0 > fcntl(socket, F_SETFL, opts)) { 38 | printf("failed to call fcntl with F_SETFL\n"); 39 | exit(1); 40 | } 41 | 42 | setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 43 | } 44 | 45 | static void _init_socket(struct connection_info *ci) { 46 | ci->socket = socket(PF_INET, SOCK_STREAM, 0); 47 | 48 | if (-1 == ci->socket) { 49 | printf("failed to create client socket\n"); 50 | exit(1); 51 | } 52 | 53 | _set_socket_nonblocking(ci->socket); 54 | 55 | struct sockaddr_in client_addr; 56 | bzero(&client_addr, sizeof(struct sockaddr_in)); 57 | client_addr.sin_len = sizeof(struct sockaddr_in); 58 | client_addr.sin_family = AF_INET; 59 | client_addr.sin_addr.s_addr = htons(INADDR_ANY); 60 | client_addr.sin_port = htons(0); 61 | 62 | if (bind(ci->socket, (struct sockaddr *) &client_addr, 63 | sizeof(struct sockaddr_in))) { 64 | printf("failed to bind socket\n"); 65 | exit(1); 66 | } 67 | } 68 | 69 | static void _fini_socket(struct connection_info *ci) { 70 | close(ci->socket); 71 | } 72 | 73 | static void *_working(void *arg) { 74 | __lock; 75 | _connection_num++; 76 | __unlock; 77 | 78 | struct connection_info *ci = (struct connection_info *) arg; 79 | ci->is_connected = 0; 80 | 81 | struct sockaddr_in server_addr; 82 | bzero(&server_addr, sizeof(struct sockaddr_in)); 83 | server_addr.sin_len = sizeof(struct sockaddr_in); 84 | server_addr.sin_family = AF_INET; 85 | server_addr.sin_port = htons(ci->port); 86 | 87 | if (0 == inet_aton(ci->ip, &server_addr.sin_addr)) { 88 | printf("failed to initialize socket ip\n"); 89 | exit(1); 90 | } 91 | 92 | while (ci->is_running) { 93 | int ret = connect(ci->socket, (struct sockaddr *) &server_addr, 94 | sizeof(struct sockaddr_in)); 95 | 96 | if (0 == ret) { 97 | ci->callback_funs.on_connected(ci->parameter); 98 | ci->is_connected = 1; 99 | } else if (0 > ret && EINPROGRESS != errno) { 100 | printf("failed to connect to destination\n"); 101 | _fini_socket(ci); 102 | goo_sleep(ci->reconnect_interval, 0); 103 | _init_socket(ci); 104 | 105 | continue; 106 | } 107 | 108 | ci->needs_reconnect = 0; 109 | fd_set fd_read_set; 110 | fd_set fd_write_set; 111 | fd_set fd_error_set; 112 | struct timeval tv; 113 | tv.tv_sec = ci->reconnect_interval; 114 | tv.tv_usec = 0; 115 | 116 | while (ci->is_running && !ci->needs_reconnect) { 117 | FD_ZERO(&fd_read_set); 118 | FD_ZERO(&fd_write_set); 119 | FD_ZERO(&fd_error_set); 120 | FD_SET(ci->socket, &fd_read_set); 121 | FD_SET(ci->socket, &fd_write_set); 122 | FD_SET(ci->socket, &fd_error_set); 123 | 124 | int ret = select(ci->socket + 1, &fd_read_set, &fd_write_set, 125 | &fd_error_set, &tv); 126 | 127 | if (0 > ret) { 128 | ci->is_connected = 0; 129 | ci->needs_reconnect = 1; 130 | ci->callback_funs.on_closed(ci->parameter); 131 | _fini_socket(ci); 132 | goo_sleep(ci->reconnect_interval, 0); 133 | _init_socket(ci); 134 | 135 | break; 136 | } else if (0 == ret) { 137 | continue; 138 | } 139 | 140 | 141 | if (FD_ISSET(ci->socket, &fd_error_set)) { 142 | ci->is_connected = 0; 143 | ci->needs_reconnect = 1; 144 | ci->callback_funs.on_error(&ci, 1, "found error on the socket " 145 | "after select\n"); 146 | _fini_socket(ci); 147 | goo_sleep(ci->reconnect_interval, 0); 148 | _init_socket(ci); 149 | 150 | break; 151 | } 152 | 153 | if (FD_ISSET(ci->socket, &fd_write_set)) { 154 | if (0 == ci->is_connected) { 155 | int status; 156 | socklen_t slen = sizeof(int); 157 | 158 | getsockopt(ci->socket, SOL_SOCKET, SO_ERROR, 159 | (void *) &status, &slen); 160 | 161 | if (0 == status) { 162 | ci->is_connected = 1; 163 | ci->callback_funs.on_connected(&ci); 164 | } else { 165 | break; 166 | } 167 | } 168 | } 169 | 170 | if (FD_ISSET(ci->socket, &fd_read_set)) { 171 | for (; ;) { 172 | if (0 == ci->pending_size) { 173 | ci->pending_size = sizeof(struct THeader); 174 | } 175 | 176 | ssize_t len = recv(ci->socket, ci->msg_buf + ci->offset, 177 | ci->pending_size - ci->offset, 0); 178 | 179 | if (0 < len) { // read data from socket buffer 180 | ci->offset += len; 181 | 182 | if (sizeof(struct THeader) == ci->offset) { 183 | struct THeader *header = (struct THeader *)ci->msg_buf; 184 | 185 | if (sizeof(struct THeader) == header->size) { 186 | ci->callback_funs.on_received(ci->parameter, 187 | ci->msg_buf, ci->offset); 188 | ci->offset = 0; 189 | ci->pending_size = 0; 190 | } else { 191 | ci->pending_size = header->size; 192 | } 193 | } else if (ci->pending_size == ci->offset){ 194 | ci->callback_funs.on_received(ci->parameter, 195 | ci->msg_buf, ci->offset); 196 | ci->offset = 0; 197 | ci->pending_size = 0; 198 | } 199 | } else if (0 > len) { 200 | if (EAGAIN != errno || EWOULDBLOCK != errno) { 201 | // error happened 202 | // the socket has been closed 203 | ci->is_connected = 0; 204 | ci->needs_reconnect = 1; 205 | ci->callback_funs.on_closed(ci->parameter); 206 | _fini_socket(ci); 207 | goo_sleep(ci->reconnect_interval, 0); 208 | _init_socket(ci); 209 | } 210 | 211 | break; 212 | } else { 213 | // the socket has been closed 214 | ci->is_connected = 0; 215 | ci->needs_reconnect = 1; 216 | ci->callback_funs.on_closed(ci->parameter); 217 | _fini_socket(ci); 218 | goo_sleep(ci->reconnect_interval, 0); 219 | _init_socket(ci); 220 | 221 | break; 222 | } 223 | } 224 | } 225 | } 226 | } 227 | 228 | ci->is_connected = 0; 229 | ci->callback_funs.on_closed(ci->parameter); 230 | _fini_socket(ci); 231 | 232 | __lock; 233 | _connection_num--; 234 | __unlock; 235 | 236 | ci->tid = 0; 237 | 238 | return NULL; 239 | } 240 | 241 | int cnn_start(struct connection_info *ci, bool join) { 242 | _init_socket(ci); 243 | 244 | ci->is_running = 1; 245 | 246 | if (0 != pthread_create(&ci->tid, NULL, _working, ci)) { 247 | printf("failed to create thread\n"); 248 | 249 | return 1; 250 | } 251 | 252 | if (join) { 253 | if (pthread_join(ci->tid, NULL)) { 254 | printf("failed to wait the thread\n"); 255 | 256 | return 2; 257 | } 258 | } else { 259 | pthread_detach(ci->tid); 260 | }; 261 | 262 | return 0; 263 | } 264 | 265 | void cnn_stop(struct connection_info *ci, bool sync) { 266 | ci->is_running = 0; 267 | 268 | if (sync) { 269 | while (ci->tid) { 270 | goo_sleep(0, 100); 271 | } 272 | } 273 | } 274 | 275 | int cnn_send(struct connection_info *ci, const unsigned char *buffer, 276 | unsigned int size) { 277 | assert(NULL != buffer); 278 | assert(0 < size); 279 | 280 | if (ci->is_connected) { 281 | ssize_t n = 0; 282 | unsigned int offset = 0; 283 | 284 | do { 285 | n = write(ci->socket, buffer + offset, size - offset); 286 | 287 | if (-1 == n) { 288 | if (EAGAIN == errno || EWOULDBLOCK == errno) { 289 | goo_sleep(0, 10); 290 | continue; 291 | } else { 292 | return -2; 293 | } 294 | } else if (0 == n) { 295 | // the socket has been closed 296 | return -2; 297 | } 298 | 299 | offset += n; 300 | } while (size > offset); 301 | 302 | printf("sent %d bytes to server\n", size); 303 | 304 | return 0; 305 | } 306 | 307 | return -1; 308 | } 309 | 310 | unsigned int cnn_get_connection_num() { 311 | return _connection_num; 312 | } 313 | -------------------------------------------------------------------------------- /clients/c/connection.h: -------------------------------------------------------------------------------- 1 | #ifndef _connection_h_ 2 | #define _connection_h_ 3 | 4 | #include 5 | 6 | struct connection_info; 7 | 8 | int cnn_start(struct connection_info *ci, bool join); 9 | 10 | void cnn_stop(struct connection_info *ci, bool sync); 11 | 12 | int cnn_send(struct connection_info *ci, const unsigned char *buffer, unsigned int size); 13 | 14 | unsigned int cnn_get_connection_num(); 15 | 16 | #endif // _connection_h_ 17 | -------------------------------------------------------------------------------- /clients/c/connection_info.h: -------------------------------------------------------------------------------- 1 | #ifndef _connection_info_h_ 2 | #define _connection_info_h_ 3 | 4 | #include 5 | 6 | #include "size.h" 7 | 8 | typedef void (*cnn_ON_CONNECTED)(void *parameter); 9 | 10 | typedef void (*cnn_ON_RECEIVED)(void *parameter, const unsigned char *data, 11 | unsigned int size); 12 | 13 | typedef void (*cnn_ON_ERROR)(void *parameter, int err_code, 14 | const char *err_desc); 15 | 16 | typedef void (*cnn_ON_CLOSED)(void *parameter); 17 | 18 | struct cnn_callback_funs { 19 | cnn_ON_CONNECTED on_connected; 20 | cnn_ON_RECEIVED on_received; 21 | cnn_ON_ERROR on_error; 22 | cnn_ON_CLOSED on_closed; 23 | }; 24 | 25 | struct connection_info { 26 | void *parameter; 27 | 28 | char ip[SIZE_IP_V4]; 29 | unsigned short port; 30 | unsigned int reconnect_interval; // seconds 31 | 32 | struct cnn_callback_funs callback_funs; 33 | 34 | // following parameters are managed by the application 35 | int socket; 36 | pthread_t tid; 37 | int is_running; 38 | int is_connected; 39 | int needs_reconnect; 40 | 41 | unsigned char msg_buf[MSG_MAX_LENGTH]; 42 | unsigned int offset; 43 | unsigned int pending_size; 44 | }; 45 | 46 | #endif // _connection_info_h_ 47 | -------------------------------------------------------------------------------- /clients/c/main.c: -------------------------------------------------------------------------------- 1 | // 2 | // main.c 3 | // RealtimeCTester 4 | // 5 | // Created by 顾笑群 on 16/5/19. 6 | // Copyright (c) 2016 Rafael Gu. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "bridge.h" 14 | 15 | static char _sessionId[17]; 16 | static unsigned long long _groupId; 17 | static char _ip[16] = "127.0.0.1"; 18 | static unsigned short _port = 10505; 19 | static unsigned int _intervalSeconds = 10; 20 | static unsigned int _buildNum = 1; 21 | 22 | static unsigned int _totalSendNum = 0; 23 | static unsigned int _totalPushNum = 0; 24 | 25 | unsigned long long on_connected() { 26 | printf("bridge: on_connect\n"); 27 | 28 | return 0; 29 | } 30 | 31 | void on_ready() { 32 | printf("bridge: on_ready\n"); 33 | 34 | if (net_worker_send(2, _groupId, "{\"ct\":2,\"mt\":1,\"uri\":\"http:\\/\\/192.168.7.55:3000\\/images\\/124\",\"dateString\":\"06-27 14:43\",\"incoming\":false}")) { 35 | printf("bridge: on_ready - failed to send message\n"); 36 | } else { 37 | _totalSendNum++; 38 | } 39 | } 40 | 41 | void on_push(unsigned char ornType, unsigned long long ornId, unsigned long long ornExtId, 42 | unsigned long long messageId, const char *json, unsigned long long stmp) { 43 | _totalPushNum++; 44 | printf("bridge: on_push(%u)|ornType:%u|ornId:%llu|ornExtId:%llu|messageId:%llu|json:%s|stmp:%llu\n", 45 | _totalPushNum, ornType, ornId, ornExtId, messageId, json, stmp); 46 | 47 | if (_totalPushNum < _totalSendNum * 8) { 48 | return; 49 | } 50 | 51 | if (net_worker_send(2, _groupId, "{\"ct\":2,\"mt\":1,\"uri\":\"http:\\/\\/192.168.7.55:3000\\/images\\/124\",\"dateString\":\"06-27 14:43\",\"incoming\":false}")) { 52 | printf("bridge: on_push - failed to send message\n"); 53 | } 54 | } 55 | 56 | void on_send_ack(unsigned long long seq, unsigned short code, 57 | unsigned long long messageId) { 58 | printf("bridge: on_send_ack|seq:%llu|code:%u|messageId:%llu\n", 59 | seq, code, messageId); 60 | 61 | if (0 == code) { 62 | _totalSendNum++; 63 | } 64 | } 65 | 66 | void on_closed() { 67 | printf("bridge: on_closed\n"); 68 | } 69 | 70 | void on_error(int err_code, const char *err_desc) { 71 | printf("bridge: %d-%s", err_code, err_desc); 72 | } 73 | 74 | int main(int argc, const char *argv[]) { 75 | if (3 > argc || 7 < argc) { 76 | printf("Usage: %s [ip(%s)] [port(%u)] [interval seconds(%u)] [build number(%u)]\n", 77 | argv[0], _ip, _port, _intervalSeconds, _buildNum); 78 | 79 | return 1; 80 | } 81 | 82 | if (16 != strlen(argv[1])) { 83 | printf("session id is a string with length of 16\n"); 84 | 85 | return 2; 86 | } 87 | 88 | memset(_sessionId, 0, 17); 89 | strncpy(_sessionId, argv[1], 16); 90 | _groupId = (unsigned long long) atoll(argv[2]); 91 | 92 | if (0 == _groupId) { 93 | printf("group id must be integer\n"); 94 | 95 | return 2; 96 | } 97 | 98 | if (3 < argc) { 99 | if (0 == argv[3][0] || 15 < strlen(argv[3])) { 100 | printf("%s is invalid ip\n", argv[3]); 101 | 102 | return 4; 103 | } 104 | 105 | memset(_ip, 0, 16); 106 | strncpy(_ip, argv[3], 15); 107 | } 108 | 109 | if (4 < argc) { 110 | _port = (unsigned short) atoi(argv[4]); 111 | 112 | if (1024 >= _port) { 113 | printf("%s is invalid port\n", argv[4]); 114 | } 115 | } 116 | 117 | if (5 < argc) { 118 | _intervalSeconds = (unsigned int) atoi(argv[5]); 119 | 120 | if (0 == _intervalSeconds) { 121 | printf("%s is invalid interval seconds\n", argv[5]); 122 | } 123 | } 124 | 125 | if (6 < argc) { 126 | _buildNum = (unsigned int) atoi(argv[6]); 127 | 128 | if (0 == _buildNum) { 129 | printf("%s is invalid build num\n", argv[6]); 130 | } 131 | } 132 | 133 | set_on_connected(on_connected); 134 | set_on_ready(on_ready); 135 | set_on_push(on_push); 136 | set_on_send_ack(on_send_ack); 137 | set_on_error(on_error); 138 | set_on_closed(on_closed); 139 | net_worker_start(_ip, _port, _intervalSeconds, _buildNum, _sessionId); 140 | 141 | return 0; 142 | } 143 | -------------------------------------------------------------------------------- /clients/c/message.h: -------------------------------------------------------------------------------- 1 | #ifndef _message_h_ 2 | #define _message_h_ 3 | 4 | #include "size.h" 5 | 6 | //////////////////////////////////// 7 | // type values in THeader 8 | //////////////////////////////////// 9 | #define MT_CONTROL 0x0001 10 | #define MT_ACCOUNT 0x0002 11 | #define MT_SERVICE 0x0003 12 | 13 | #define MT_SIGN_ACK 0x0010 14 | #define MT_SIGN_LOG 0x0020 15 | #define MT_SIGN_FEE 0x0040 16 | #define MT_SIGN_GRP 0x0080 17 | 18 | //////////////////////////////////// 19 | // cmd values in THeader 20 | //////////////////////////////////// 21 | // network messages: 0xXXXX except 0x8XXX 22 | #define MC_HAND_SHAKE 0x0001 23 | #define MC_HEART_BEAT 0x0002 24 | 25 | #define MC_SEND_MSG 0x0003 26 | #define MC_PUSH_MSG 0x0004 27 | 28 | //////////////////////////////////// 29 | // lang values in THeader 30 | //////////////////////////////////// 31 | #define ML_CN 0x01 32 | #define ML_TW 0x02 33 | #define ML_EN 0x03 34 | 35 | //////////////////////////////////// 36 | // PDUs 37 | //////////////////////////////////// 38 | #pragma pack(1) 39 | 40 | struct THeader { 41 | unsigned short size; 42 | unsigned short type; 43 | unsigned short cmd; 44 | unsigned short ver; 45 | unsigned char lang; 46 | unsigned int seq; 47 | unsigned long long stmp; 48 | unsigned long long ext; 49 | }; 50 | 51 | struct TAck { 52 | unsigned short code; 53 | }; 54 | 55 | struct TPDUHandShake { 56 | struct THeader header; 57 | unsigned int build; 58 | unsigned long long lastUpdate; 59 | char sessionId[SIZE_SESSION_ID]; 60 | }; 61 | 62 | struct TPDUHandShakeAck { 63 | struct THeader header; 64 | struct TAck ack; 65 | }; 66 | 67 | struct TPDUHeartBeat { 68 | struct THeader header; 69 | }; 70 | 71 | struct TPDUHeartBeatAck { 72 | struct THeader header; 73 | struct TAck ack; 74 | }; 75 | 76 | struct TPDUSendMsg { 77 | struct THeader header; 78 | unsigned char dstType; // 1 - user, 2 - group 79 | unsigned long long dstId; 80 | char json[SIZE_JSON]; 81 | }; 82 | 83 | struct TPDUSendMsgAck { 84 | struct THeader header; 85 | struct TAck ack; 86 | unsigned long long messageId; 87 | }; 88 | 89 | struct TPDUPushMsg { 90 | struct THeader header; 91 | unsigned char ornType; // 1 - user, 2- group 92 | unsigned long long ornId; 93 | unsigned long long ornExtId; 94 | unsigned long long messageId; 95 | char json[SIZE_JSON]; 96 | }; 97 | 98 | struct TPDUPushMsgAck { 99 | struct THeader header; 100 | struct TAck ack; 101 | }; 102 | 103 | #pragma pack() 104 | 105 | #endif // _message_h_ 106 | -------------------------------------------------------------------------------- /clients/c/size.h: -------------------------------------------------------------------------------- 1 | #ifndef _size_h_ 2 | #define _size_h_ 3 | 4 | #define MSG_MAX_LENGTH 512 5 | 6 | #define SIZE_IP_V4 16 7 | #define SIZE_SESSION_ID 17 8 | #define SIZE_JSON 385 9 | 10 | #endif // _size_h_ 11 | -------------------------------------------------------------------------------- /clients/java/src/com/example/conf/Config.java: -------------------------------------------------------------------------------- 1 | package com.exmaple.conf; 2 | 3 | public class Config { 4 | 5 | public static final String URL = "172.16.246.136"; 6 | public static final int PORT = 10505; 7 | 8 | public static final String LOG_TAG = "gMaxLinkedClientTester"; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /clients/java/src/com/example/conf/Size.java: -------------------------------------------------------------------------------- 1 | package com.exmaple.conf; 2 | 3 | public interface Size { 4 | 5 | int SESSION_ID = 17; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /clients/java/src/com/example/msg/Ack.java: -------------------------------------------------------------------------------- 1 | package com.exmaple.msg; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import com.avcon.tool.NetworkDataHelper; 6 | 7 | public final class Ack implements MsgSegment { 8 | 9 | public Ack() { 10 | } 11 | 12 | public Ack(int code) { 13 | this.code = code; 14 | } 15 | 16 | public int segmentSize() { 17 | return 2; 18 | } 19 | 20 | public boolean parse(ByteBuffer buffer) { 21 | if (null == buffer || this.segmentSize() > buffer.capacity() - buffer.position()) { 22 | return false; 23 | } 24 | 25 | this.code = NetworkDataHelper.getUShort(buffer); 26 | 27 | return true; 28 | } 29 | 30 | public boolean compose(ByteBuffer buffer) { 31 | if (null == buffer || this.segmentSize() > buffer.capacity() - buffer.position()) { 32 | return false; 33 | } 34 | 35 | NetworkDataHelper.setUShort(buffer, this.code); 36 | 37 | return true; 38 | } 39 | 40 | // Fields in network message header 41 | public int code = 0; // unsigned short 42 | 43 | } 44 | -------------------------------------------------------------------------------- /clients/java/src/com/example/msg/Header.java: -------------------------------------------------------------------------------- 1 | package com.exmaple.msg; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import com.avcon.tool.NetworkDataHelper; 6 | 7 | public final class Header implements MsgSegment { 8 | 9 | public Header() { 10 | } 11 | 12 | public Header(int type, int cmd, int ver, short lang, long seq, long stmp, long ext) { 13 | this.type = type; 14 | this.cmd = cmd; 15 | this.ver = ver; 16 | this.lang = lang; 17 | this.seq = seq; 18 | this.stmp = stmp; 19 | this.ext = ext; 20 | } 21 | 22 | public int segmentSize() { 23 | return 29; 24 | } 25 | 26 | public boolean parse(ByteBuffer buffer) { 27 | if (null == buffer || this.segmentSize() > buffer.capacity() - buffer.position()) { 28 | return false; 29 | } 30 | 31 | this.size = NetworkDataHelper.getUShort(buffer); 32 | this.type = NetworkDataHelper.getUShort(buffer); 33 | this.cmd = NetworkDataHelper.getUShort(buffer); 34 | this.ver = NetworkDataHelper.getUShort(buffer); 35 | this.lang = NetworkDataHelper.getUByte(buffer); 36 | this.seq = NetworkDataHelper.getUInt(buffer); 37 | this.stmp = NetworkDataHelper.getLong(buffer); 38 | this.ext = NetworkDataHelper.getLong(buffer); 39 | 40 | return true; 41 | } 42 | 43 | public boolean compose(ByteBuffer buffer) { 44 | if (null == buffer || this.segmentSize() > buffer.capacity() - buffer.position()) { 45 | return false; 46 | } 47 | 48 | NetworkDataHelper.setUShort(buffer, this.size); 49 | NetworkDataHelper.setUShort(buffer, this.type); 50 | NetworkDataHelper.setUShort(buffer, this.cmd); 51 | NetworkDataHelper.setUShort(buffer, this.ver); 52 | NetworkDataHelper.setUByte(buffer, this.lang); 53 | NetworkDataHelper.setUInt(buffer, this.seq); 54 | NetworkDataHelper.setLong(buffer, this.stmp); 55 | NetworkDataHelper.setLong(buffer, this.ext); 56 | 57 | return true; 58 | } 59 | 60 | // Fields in network message header 61 | public int size = 0; // unsigned short 62 | public int type = 0; // unsigned short 63 | public int cmd = 0; // unsigned short 64 | public int ver = 0; // unsigned short 65 | public short lang = 0; // unsigned char 66 | public long seq = 0; // unsigned long 67 | public long stmp = 0; // unsigned long 68 | public long ext = 0; // unsigned long 69 | 70 | } 71 | -------------------------------------------------------------------------------- /clients/java/src/com/example/msg/MsgDefine.java: -------------------------------------------------------------------------------- 1 | package com.exmaple.msg; 2 | 3 | public interface MsgDefine { 4 | 5 | int MSG_MAX_LENGTH = 512; 6 | 7 | //////////////////////////////////// 8 | // type values in THeader 9 | //////////////////////////////////// 10 | int MT_CONTROL = 0x0001; // unsigned short 11 | int MT_ACCOUNT = 0x0002; // unsigned short 12 | int MT_SERVICE = 0x0003; // unsigned short 13 | 14 | int MT_SIGN_ACK = 0x0010; // unsigned short 15 | int MT_SIGN_LOG = 0x0020; // unsigned short 16 | int MT_SIGN_FEE = 0x0040; // unsigned short 17 | 18 | //////////////////////////////////// 19 | // cmd values in THeader 20 | //////////////////////////////////// 21 | // network messages: 0xXXXX except 0x8XXX 22 | int MC_HAND_SHAKE = 0x0001; // unsigned short 23 | int MC_HEART_BEAT = 0x0002; // unsigned short 24 | 25 | //////////////////////////////////// 26 | // lang values in THeader 27 | //////////////////////////////////// 28 | short ML_CN = 0x01; // unsigned char 29 | short ML_TW = 0x02; // unsigned char 30 | short ML_EN = 0x03; // unsigned char 31 | 32 | } 33 | -------------------------------------------------------------------------------- /clients/java/src/com/example/msg/MsgHandshake.java: -------------------------------------------------------------------------------- 1 | package com.exmaple.msg; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import com.avcon.conf.Size; 6 | import com.avcon.tool.NetworkDataHelper; 7 | 8 | public class MsgHandshake implements MsgSegment { 9 | 10 | public MsgHandshake(long build, String sessionId) { 11 | this.build = build; 12 | this.sessionId = sessionId; 13 | } 14 | 15 | @Override 16 | public int segmentSize() { 17 | return 4 + Size.SESSION_ID; 18 | } 19 | 20 | @Override 21 | public boolean parse(ByteBuffer buffer) { 22 | assert(false); 23 | return true; 24 | } 25 | 26 | @Override 27 | public boolean compose(ByteBuffer buffer) { 28 | if (null == buffer || segmentSize() > buffer.capacity() - buffer.position()) { 29 | return false; 30 | } 31 | 32 | NetworkDataHelper.setUInt(buffer, this.build); 33 | NetworkDataHelper.setString(buffer, this.sessionId); 34 | 35 | return true; 36 | } 37 | 38 | 39 | // Fields in network message 40 | public long build = 0; // unsigned int 41 | public String sessionId; // md5 with 16 length and 1 for '\0' 42 | } 43 | -------------------------------------------------------------------------------- /clients/java/src/com/example/msg/MsgHelper.java: -------------------------------------------------------------------------------- 1 | package com.exmaple.msg; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | public class MsgHelper { 6 | 7 | public static Header parseHeader(ByteBuffer bufferMsg) { 8 | Header header = new Header(); 9 | 10 | if (header.parse(bufferMsg)) { 11 | return header; 12 | } 13 | 14 | return null; 15 | } 16 | 17 | public static Ack parseAck(ByteBuffer bufferMsg) { 18 | Ack ack = new Ack(); 19 | 20 | if (ack.parse(bufferMsg)) { 21 | return ack; 22 | } 23 | 24 | return null; 25 | } 26 | 27 | public static byte[] compose(Header header, Ack ack, MsgSegment body) { 28 | header.size = header.segmentSize(); 29 | 30 | if (null != ack) { 31 | header.type |= MsgDefine.MT_SIGN_ACK; 32 | header.size += ack.segmentSize(); 33 | } 34 | 35 | if (null != body) { 36 | header.size += body.segmentSize(); 37 | } 38 | 39 | ByteBuffer buffer = ByteBuffer.allocate(header.size); 40 | 41 | if (!header.compose(buffer)) { 42 | return null; 43 | } 44 | 45 | if (null != ack) { 46 | if (!ack.compose(buffer)) { 47 | return null; 48 | } 49 | } 50 | 51 | if (null != body) { 52 | if (!body.compose(buffer)) { 53 | return null; 54 | } 55 | } 56 | 57 | return buffer.array(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /clients/java/src/com/example/msg/MsgReq.java: -------------------------------------------------------------------------------- 1 | package com.exmaple.msg; 2 | 3 | public interface MsgReq { 4 | 5 | public byte[] getBytes(); 6 | } 7 | -------------------------------------------------------------------------------- /clients/java/src/com/example/msg/MsgRes.java: -------------------------------------------------------------------------------- 1 | package com.exmaple.msg; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | public interface MsgRes { 6 | 7 | public boolean parseBody(ByteBuffer buffer); 8 | } 9 | -------------------------------------------------------------------------------- /clients/java/src/com/example/msg/MsgSegment.java: -------------------------------------------------------------------------------- 1 | package com.exmaple.msg; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | public interface MsgSegment { 6 | 7 | int segmentSize(); 8 | boolean parse(ByteBuffer buffer); 9 | boolean compose(ByteBuffer buffer); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /clients/java/src/com/example/network/Connection.java: -------------------------------------------------------------------------------- 1 | package com.exmaple.network; 2 | 3 | import java.io.InterruptedIOException; 4 | import java.net.InetSocketAddress; 5 | import java.net.Socket; 6 | import java.net.SocketException; 7 | import java.net.SocketTimeoutException; 8 | import java.net.UnknownHostException; 9 | import java.nio.ByteBuffer; 10 | 11 | import com.avcon.conf.Config; 12 | import com.avcon.msg.MsgDefine; 13 | import com.avcon.tool.Log; 14 | import com.avcon.tool.NetworkDataHelper; 15 | 16 | public class Connection implements Runnable { 17 | 18 | // Running formally 19 | private final static int CONNECTION_TIME_OUT = 10000; // ms 20 | private final static int WAIT_TIMES = 1; 21 | private final static int RW_TIME_OUT = 3000; // ms 22 | private final static int TIME_OUT = 500; // ms 23 | 24 | // Used to send message to service and change status 25 | private ConnectionDelegate listener = null; 26 | 27 | private Socket socket = null; 28 | // Maybe it's just domain name instead of ip 29 | private String server = Config.URL; 30 | // It will keep the real ip 31 | private String ip = null; 32 | private int port = Config.PORT; 33 | private InetSocketAddress address = null; 34 | 35 | private boolean isRun = false; 36 | private boolean isRunning = false; 37 | 38 | public Connection(ConnectionDelegate listener) { 39 | assert (null != listener); 40 | this.listener = listener; 41 | } 42 | 43 | public synchronized boolean isRun() { 44 | return isRun; 45 | } 46 | 47 | public synchronized void start() { 48 | if (isRun) { 49 | return; 50 | } 51 | 52 | isRun = true; 53 | 54 | new Thread(this).start(); 55 | } 56 | 57 | public synchronized void stop() { 58 | if (!isRun) { 59 | return; 60 | } 61 | 62 | isRun = false; 63 | 64 | while (true == isRunning) { 65 | try { 66 | Thread.sleep(TIME_OUT); 67 | } catch (InterruptedException e) { 68 | e.printStackTrace(); 69 | } 70 | } 71 | 72 | close(); 73 | listener.onDisconnected(false); 74 | } 75 | 76 | public void set(String server, int port) { 77 | assert (null != server && 0 < server.length()); 78 | assert (1024 < port && 65535 > port); 79 | this.server = server; 80 | this.port = port; 81 | } 82 | 83 | public synchronized boolean send(byte[] msg) { 84 | if (isRun && null != socket) { 85 | try { 86 | socket.getOutputStream().write(msg); 87 | socket.getOutputStream().flush(); 88 | 89 | return true; 90 | } catch (SocketTimeoutException e) { 91 | assert false; 92 | } catch (Exception e) { 93 | e.printStackTrace(); 94 | } 95 | } 96 | 97 | return false; 98 | } 99 | 100 | public void run() { 101 | isRunning = true; 102 | 103 | while (isRun) { 104 | if (false == running()) { 105 | int i = WAIT_TIMES; 106 | 107 | while (0 < i-- && isRun) { 108 | try { 109 | Thread.sleep(CONNECTION_TIME_OUT); 110 | } catch (InterruptedException e) { 111 | e.printStackTrace(); 112 | } 113 | } 114 | } 115 | } 116 | 117 | isRunning = false; 118 | } 119 | 120 | private final int sizeSize = 2; // the size of size field in header 121 | private ByteBuffer bufferSize = ByteBuffer.allocate(sizeSize); 122 | 123 | private ByteBuffer bufferMsg = null; 124 | private int offset = 0; 125 | private int pendingSize = 0; 126 | 127 | private void close() { 128 | Log.d(Config.LOG_TAG, "Close socket"); 129 | 130 | if (null != socket) { 131 | try { 132 | if (socket.isConnected()) { 133 | // looks there are bugs in sdk 2.1 when call socket.close, 134 | // and shutdown input and output first 135 | socket.shutdownInput(); 136 | socket.shutdownOutput(); 137 | socket.close(); 138 | 139 | Log.d(Config.LOG_TAG, "Close socket successfully"); 140 | } 141 | } catch (Exception e) { 142 | e.printStackTrace(); 143 | } finally { 144 | socket = null; 145 | } 146 | } 147 | 148 | bufferMsg = null; 149 | offset = 0; 150 | pendingSize = 0; 151 | } 152 | 153 | private boolean running() { 154 | if (null == socket) { 155 | socket = new Socket(); 156 | 157 | try { 158 | socket.setSoTimeout(RW_TIME_OUT); 159 | } catch (SocketException e) { 160 | e.printStackTrace(); 161 | } 162 | 163 | try { 164 | ip = java.net.InetAddress.getByName(server).getHostAddress(); 165 | } catch (UnknownHostException e) { 166 | e.printStackTrace(); 167 | 168 | return false; 169 | } 170 | 171 | address = new InetSocketAddress(ip, port); 172 | } 173 | 174 | if (!isRun) { 175 | return false; 176 | } 177 | 178 | if (!socket.isConnected()) { 179 | try { 180 | socket.connect(address, CONNECTION_TIME_OUT); 181 | } catch (IllegalArgumentException e) { 182 | // the given SocketAddress is invalid or not supported 183 | // or 184 | // the timeout value is negative 185 | assert (false); 186 | } catch (Exception e) { 187 | // error occurs while connecting 188 | e.printStackTrace(); 189 | close(); 190 | listener.onError(); 191 | 192 | return false; 193 | } 194 | 195 | listener.onConnected(this); 196 | } 197 | 198 | int n = 0; 199 | 200 | // a little complex here 201 | for (;;) { 202 | if (!isRun) { 203 | return false; 204 | } 205 | 206 | if (0 == pendingSize) { 207 | try { 208 | n = socket.getInputStream().read(bufferSize.array(), offset, bufferSize.capacity() - offset); 209 | } catch (InterruptedIOException e) { 210 | break; 211 | } catch (Exception e) { 212 | e.printStackTrace(); 213 | close(); 214 | listener.onDisconnected(true); 215 | 216 | return false; 217 | } 218 | 219 | if (-1 == n) { 220 | // got the end of the stream 221 | Log.d(Config.LOG_TAG, "The connection has been closed"); 222 | close(); 223 | listener.onDisconnected(true); 224 | 225 | return false; 226 | } 227 | 228 | offset += n; 229 | 230 | if (sizeSize == offset) { 231 | bufferSize.position(0); 232 | pendingSize = NetworkDataHelper.getUShort(bufferSize); 233 | 234 | if (MsgDefine.MSG_MAX_LENGTH < pendingSize) { 235 | // get invalid message 236 | close(); 237 | listener.onError(); 238 | 239 | return false; 240 | } 241 | 242 | bufferMsg = ByteBuffer.allocate(pendingSize); 243 | bufferMsg.put(bufferSize); 244 | } 245 | } else { 246 | assert (null != bufferMsg); 247 | 248 | try { 249 | n = socket.getInputStream().read(bufferMsg.array(), offset, bufferMsg.capacity() - offset); 250 | } catch (Exception e) { 251 | e.printStackTrace(); 252 | close(); 253 | listener.onDisconnected(true); 254 | 255 | return false; 256 | } 257 | 258 | if (-1 == n) { 259 | Log.d(Config.LOG_TAG, "Server shut down the connection"); 260 | close(); 261 | listener.onDisconnected(true); 262 | 263 | return false; 264 | } 265 | 266 | offset+= n; 267 | 268 | if (pendingSize == offset) { 269 | if (!listener.onMessage(bufferMsg)) { 270 | Log.e(Config.LOG_TAG, "The message is invalid"); 271 | close(); 272 | listener.onError(); 273 | 274 | return false; 275 | } 276 | 277 | bufferMsg = null; 278 | offset = 0; 279 | pendingSize = 0; 280 | } 281 | } 282 | } 283 | 284 | return true; 285 | } 286 | } -------------------------------------------------------------------------------- /clients/java/src/com/example/network/ConnectionDelegate.java: -------------------------------------------------------------------------------- 1 | package com.exmaple.network; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | public interface ConnectionDelegate { 6 | 7 | public void onConnected(Connection cnn); 8 | 9 | public void onDisconnected(boolean byRemote); 10 | 11 | public boolean onMessage(ByteBuffer bufferMsg); 12 | 13 | public void onError(); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /clients/java/src/com/example/network/Tester.java: -------------------------------------------------------------------------------- 1 | package com.exmaple.network; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | import com.avcon.msg.Ack; 6 | import com.avcon.msg.Header; 7 | import com.avcon.msg.MsgDefine; 8 | import com.avcon.msg.MsgHandshake; 9 | import com.avcon.msg.MsgHelper; 10 | import com.avcon.tool.Log; 11 | 12 | public class Tester implements ConnectionDelegate { 13 | 14 | private Connection cnn = null; 15 | 16 | @Override 17 | public void onConnected(Connection cnn) { 18 | Log.d("Tester", "onConnected"); 19 | 20 | if (null == this.cnn) { 21 | this.cnn = cnn; 22 | } 23 | 24 | Header h = new Header(MsgDefine.MT_CONTROL, MsgDefine.MC_HAND_SHAKE, 0x0100, (short) 1, 0, 25 | System.currentTimeMillis(), 0); 26 | MsgHandshake b = new MsgHandshake(1, "ABCDEFGHIJKLMNOP"); 27 | 28 | this.cnn.send(MsgHelper.compose(h, null, b)); 29 | Log.d("Tester", "send handshake"); 30 | } 31 | 32 | @Override 33 | public void onDisconnected(boolean byRemote) { 34 | Log.d("Tester", "onDisconnected"); 35 | } 36 | 37 | @Override 38 | public boolean onMessage(ByteBuffer bufferMsg) { 39 | Header header = MsgHelper.parseHeader(bufferMsg); 40 | Ack ack = null; 41 | 42 | if (null == header) { 43 | Log.e("Tester", "invalid header"); 44 | return false; 45 | } 46 | 47 | if (MsgDefine.MT_SIGN_ACK == (header.type & MsgDefine.MT_SIGN_ACK)) { 48 | ack = MsgHelper.parseAck(bufferMsg); 49 | 50 | if (null == ack) { 51 | Log.e("Tester", "invalid ack"); 52 | return false; 53 | } 54 | } 55 | 56 | switch (header.cmd) { 57 | case MsgDefine.MC_HAND_SHAKE: 58 | Log.d("Tester", "get handshake"); 59 | 60 | if (null == ack || 0 != ack.code) { 61 | Log.e("Tester", "get wrong ack for handshake, stop the connection"); 62 | 63 | if (null != this.cnn) { 64 | this.cnn.stop(); 65 | } 66 | } else { 67 | Log.d("Tester", "handshake successfully"); 68 | } 69 | 70 | break; 71 | case MsgDefine.MC_HEART_BEAT: { 72 | Log.d("Tester", "get heartbeat"); 73 | 74 | Header h = new Header(MsgDefine.MT_CONTROL, MsgDefine.MC_HEART_BEAT, 0x0100, (short) 1, header.seq, 75 | System.currentTimeMillis(), 0); 76 | Ack a = new Ack(0); 77 | 78 | if (null != this.cnn) { 79 | this.cnn.send(MsgHelper.compose(h, a, null)); 80 | Log.d("Tester", "send heartbeat"); 81 | } 82 | } 83 | 84 | break; 85 | default: 86 | Log.e("Tester", "unknown message"); 87 | return false; 88 | } 89 | 90 | return true; 91 | } 92 | 93 | @Override 94 | public void onError() { 95 | Log.d("Tester", "onError"); 96 | } 97 | 98 | public static void main(String[] args) { 99 | Tester tester = new Tester(); 100 | Connection cnn = new Connection(tester); 101 | 102 | cnn.start(); 103 | 104 | do { 105 | try { 106 | Thread.sleep(1000); 107 | } catch (Exception e) { 108 | 109 | } 110 | } while (cnn.isRun()); 111 | 112 | cnn.stop(); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /clients/java/src/com/example/tool/Log.java: -------------------------------------------------------------------------------- 1 | package com.avcon.tool; 2 | 3 | import java.util.Date; 4 | import java.text.SimpleDateFormat; 5 | 6 | public class Log { 7 | 8 | public static void d(String tag, String info) { 9 | SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 10 | System.out.println("[" + df.format(new Date()) + "]" + tag + " : " + info); 11 | } 12 | 13 | public static void e(String tag, String info) { 14 | SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 15 | System.out.println("[" + df.format(new Date()) + "]" + "* ERROR * : " + info); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /clients/java/src/com/example/tool/NetworkDataHelper.java: -------------------------------------------------------------------------------- 1 | package com.exmaple.tool; 2 | 3 | import java.lang.reflect.Array; 4 | import java.nio.ByteBuffer; 5 | import java.nio.charset.Charset; 6 | 7 | public class NetworkDataHelper { 8 | 9 | public static byte getSByte(ByteBuffer buffer) { 10 | 11 | assert (buffer.capacity() - buffer.position() >= 1); 12 | 13 | return buffer.get(); 14 | } 15 | 16 | public static short getUByte(ByteBuffer buffer) { 17 | 18 | assert (buffer.capacity() - buffer.position() >= 1); 19 | 20 | return (short) (buffer.get() & 0x000000ff); 21 | } 22 | 23 | public static short getSShort(ByteBuffer buffer) { 24 | 25 | assert (buffer.capacity() - buffer.position() >= 2); 26 | 27 | byte[] data = new byte[2]; 28 | 29 | data[1] = buffer.get(); 30 | data[0] = buffer.get(); 31 | 32 | return (short) (((data[0] << 8) & 0x0000ff00) | (data[1] & 0x000000ff)); 33 | } 34 | 35 | public static int getUShort(ByteBuffer buffer) { 36 | 37 | assert (buffer.capacity() - buffer.position() >= 2); 38 | 39 | byte[] data = new byte[2]; 40 | 41 | data[1] = buffer.get(); 42 | data[0] = buffer.get(); 43 | 44 | return ((data[0] << 8) & 0x0000ff00) | (data[1] & 0x000000ff); 45 | } 46 | 47 | public static int getSInt(ByteBuffer buffer) { 48 | 49 | assert (buffer.capacity() - buffer.position() >= 4); 50 | 51 | byte[] data = new byte[4]; 52 | 53 | data[3] = buffer.get(); 54 | data[2] = buffer.get(); 55 | data[1] = buffer.get(); 56 | data[0] = buffer.get(); 57 | 58 | return ((data[0] << 24) & 0xff000000) | ((data[1] << 16) & 0x00ff0000) | ((data[2] << 8) & 0x0000ff00) 59 | | (data[3] & 0x000000ff); 60 | } 61 | 62 | public static long getUInt(ByteBuffer buffer) { 63 | 64 | assert (buffer.capacity() - buffer.position() >= 4); 65 | 66 | byte[] data = new byte[4]; 67 | 68 | data[3] = buffer.get(); 69 | data[2] = buffer.get(); 70 | data[1] = buffer.get(); 71 | data[0] = buffer.get(); 72 | 73 | return ((data[0] << 24) & 0xff000000l) | ((data[1] << 16) & 0x00ff0000l) | ((data[2] << 8) & 0x0000ff00l) 74 | | (data[3] & 0x000000ffl); 75 | } 76 | 77 | public static long getLong(ByteBuffer buffer) { 78 | 79 | assert (buffer.capacity() - buffer.position() >= 8); 80 | 81 | long[] data = new long[8]; 82 | 83 | data[7] = buffer.get(); 84 | data[6] = buffer.get(); 85 | data[5] = buffer.get(); 86 | data[4] = buffer.get(); 87 | data[3] = buffer.get(); 88 | data[2] = buffer.get(); 89 | data[1] = buffer.get(); 90 | data[0] = buffer.get(); 91 | 92 | return ((data[0] << 56) & 0xff00000000000000l) | ((data[1] << 48) & 0x00ff000000000000l) 93 | | ((data[2] << 40) & 0x0000ff0000000000l) | ((data[3] << 32) & 0x000000ff00000000l) 94 | | ((data[4] << 24) & 0x00000000ff000000l) | ((data[5] << 16) & 0x0000000000ff0000l) 95 | | ((data[6] << 8) & 0x000000000000ff00l) | (data[7] & 0x00000000000000ffl); 96 | } 97 | 98 | public static float getFloat(ByteBuffer buffer) { 99 | 100 | assert (buffer.capacity() - buffer.position() >= 4); 101 | 102 | byte[] data = new byte[4]; 103 | 104 | data[3] = buffer.get(); 105 | data[2] = buffer.get(); 106 | data[1] = buffer.get(); 107 | data[0] = buffer.get(); 108 | 109 | return Float.intBitsToFloat(((data[0] << 24) & 0xff000000) | ((data[1] << 16) & 0x00ff0000) 110 | | ((data[2] << 8) & 0x0000ff00) | (data[3] & 0x000000ff)); 111 | 112 | } 113 | 114 | public static double getDouble(ByteBuffer buffer) { 115 | 116 | assert (buffer.capacity() - buffer.position() >= 8); 117 | 118 | long[] data = new long[8]; 119 | 120 | data[7] = buffer.get(); 121 | data[6] = buffer.get(); 122 | data[5] = buffer.get(); 123 | data[4] = buffer.get(); 124 | data[3] = buffer.get(); 125 | data[2] = buffer.get(); 126 | data[1] = buffer.get(); 127 | data[0] = buffer.get(); 128 | 129 | return Double.longBitsToDouble(((data[0] << 56) & 0xff00000000000000l) | ((data[1] << 48) & 0x00ff000000000000l) 130 | | ((data[2] << 40) & 0x0000ff0000000000l) | ((data[3] << 32) & 0x000000ff00000000l) 131 | | ((data[4] << 24) & 0x00000000ff000000l) | ((data[5] << 16) & 0x0000000000ff0000l) 132 | | ((data[6] << 8) & 0x000000000000ff00l) | (data[7] & 0x00000000000000ffl)); 133 | } 134 | 135 | public static void setSByte(ByteBuffer buffer, byte data) { 136 | 137 | assert (buffer.capacity() - buffer.position() >= 1); 138 | 139 | buffer.put(data); 140 | } 141 | 142 | public static void setUByte(ByteBuffer buffer, short data) { 143 | 144 | assert (buffer.capacity() - buffer.position() >= 1); 145 | 146 | buffer.put((byte) (data & 0x000000ff)); 147 | } 148 | 149 | public static void setSShort(ByteBuffer buffer, short data) { 150 | 151 | assert (buffer.capacity() - buffer.position() >= 2); 152 | 153 | buffer.put((byte) (data & 0x000000ff)); 154 | buffer.put((byte) ((data & 0x0000ff00) >> 8)); 155 | } 156 | 157 | public static void setUShort(ByteBuffer buffer, int data) { 158 | 159 | assert (buffer.capacity() - buffer.position() >= 2); 160 | 161 | buffer.put((byte) (data & 0x000000ff)); 162 | buffer.put((byte) ((data & 0x0000ff00) >> 8)); 163 | } 164 | 165 | public static void setSInt(ByteBuffer buffer, int data) { 166 | 167 | assert (buffer.capacity() - buffer.position() >= 4); 168 | 169 | buffer.put((byte) (data & 0x000000ffl)); 170 | buffer.put((byte) ((data & 0x0000ff00l) >> 8)); 171 | buffer.put((byte) ((data & 0x00ff0000l) >> 16)); 172 | buffer.put((byte) ((data & 0xff000000l) >> 24)); 173 | } 174 | 175 | public static void setUInt(ByteBuffer buffer, long data) { 176 | 177 | assert (buffer.capacity() - buffer.position() >= 4); 178 | 179 | buffer.put((byte) (data & 0x000000ffl)); 180 | buffer.put((byte) ((data & 0x0000ff00l) >> 8)); 181 | buffer.put((byte) ((data & 0x00ff0000l) >> 16)); 182 | buffer.put((byte) ((data & 0xff000000l) >> 24)); 183 | } 184 | 185 | public static void setLong(ByteBuffer buffer, long data) { 186 | 187 | assert (buffer.capacity() - buffer.position() >= 8); 188 | 189 | buffer.put((byte) (data & 0x00000000000000ffl)); 190 | buffer.put((byte) ((data & 0x000000000000ff00l) >>> 8)); 191 | buffer.put((byte) ((data & 0x0000000000ff0000l) >>> 16)); 192 | buffer.put((byte) ((data & 0x00000000ff000000l) >>> 24)); 193 | buffer.put((byte) ((data & 0x000000ff00000000l) >>> 32)); 194 | buffer.put((byte) ((data & 0x0000ff0000000000l) >>> 40)); 195 | buffer.put((byte) ((data & 0x00ff000000000000l) >>> 48)); 196 | buffer.put((byte) ((data & 0xff00000000000000l) >>> 56)); 197 | } 198 | 199 | public static void setFloat(ByteBuffer buffer, float data) { 200 | 201 | assert (buffer.capacity() - buffer.position() >= 4); 202 | 203 | buffer.put((byte) (Float.floatToIntBits(data) & 0x000000ff)); 204 | buffer.put((byte) ((Float.floatToIntBits(data) & 0x0000ff00) >>> 8)); 205 | buffer.put((byte) ((Float.floatToIntBits(data) & 0x00ff0000) >>> 16)); 206 | buffer.put((byte) ((Float.floatToIntBits(data) & 0xff000000) >>> 24)); 207 | } 208 | 209 | public static void setDouble(ByteBuffer buffer, double data) { 210 | 211 | assert (buffer.capacity() - buffer.position() >= 8); 212 | 213 | buffer.put((byte) (Double.doubleToLongBits(data) & 0x00000000000000ffl)); 214 | buffer.put((byte) ((Double.doubleToLongBits(data) & 0x000000000000ff00l) >>> 8)); 215 | buffer.put((byte) ((Double.doubleToLongBits(data) & 0x0000000000ff0000l) >>> 16)); 216 | buffer.put((byte) ((Double.doubleToLongBits(data) & 0x00000000ff000000l) >>> 24)); 217 | buffer.put((byte) ((Double.doubleToLongBits(data) & 0x000000ff00000000l) >>> 32)); 218 | buffer.put((byte) ((Double.doubleToLongBits(data) & 0x0000ff0000000000l) >>> 40)); 219 | buffer.put((byte) ((Double.doubleToLongBits(data) & 0x00ff000000000000l) >>> 48)); 220 | buffer.put((byte) ((Double.doubleToLongBits(data) & 0xff00000000000000l) >>> 56)); 221 | } 222 | 223 | public static String getString(ByteBuffer buffer, int size) { 224 | 225 | assert (0 < size); 226 | assert (buffer.capacity() - buffer.position() >= size); 227 | 228 | ByteBuffer buf = ByteBuffer.allocate(size); 229 | 230 | buffer.get(buf.array(), 0, size); 231 | 232 | try { 233 | return Charset.defaultCharset().newDecoder().decode(buf).toString().trim(); 234 | } catch (Exception e) { 235 | e.printStackTrace(); 236 | assert (false); 237 | 238 | return ""; 239 | } 240 | } 241 | 242 | public static void setString(ByteBuffer buffer, String data) { 243 | 244 | assert (null != data); 245 | 246 | byte[] bytes = data.getBytes(); 247 | assert (buffer.capacity() - buffer.position() >= Array.getLength(bytes)); 248 | 249 | buffer.put(bytes); 250 | buffer.put((byte) 0); 251 | } 252 | } -------------------------------------------------------------------------------- /gMaxLinked.conf: -------------------------------------------------------------------------------- 1 | [app] 2 | run_as_daemon= 3 | name= 4 | base_build= 5 | protocol_version= 6 | listen_ip= 7 | listen_port= 8 | epoll_wait_event_num= 9 | node_group_num= 10 | node_group_size= 11 | message_max_num_in_queue= 12 | thread_stack_size= 13 | heartbeat_interval= 14 | 15 | [msg] 16 | valid_duration= 17 | valid_number= 18 | 19 | [redis] 20 | host= 21 | port= 22 | timeout= 23 | auth= 24 | db= 25 | -------------------------------------------------------------------------------- /gMaxLinked.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export LOG4C_RCPATH=/usr/local/etc 3 | cd /usr/local/bin 4 | ./gMaxLinked ../etc/gMaxLinked.conf -------------------------------------------------------------------------------- /gMaxLinkedd: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # gMaxLinkedd 4 | # 5 | # chkconfig: - 80 20 6 | # processname: gMaxLinked 7 | # config: /etc/gMaxLinked.conf 8 | # pidfile: /var/run/gMaxLinked.pid 9 | # description: Starts and stops the gMaxLinked daemon. 10 | 11 | # Source function library. 12 | . /etc/rc.d/init.d/functions 13 | 14 | name=gMaxLinked 15 | run=${RUN-/usr/local/sbin/gMaxLinked} 16 | config=${CONFIG-/etc/gMaxLinked.conf} 17 | pidfile=${PDIFILE-/var/run/gMaxLinked.pid} 18 | lockfile=${LOCKFILE-/var/lock/subsys/gMaxLinked} 19 | 20 | start() { 21 | [ -f $config ] || exit 6 22 | [ -x $exec ] || exit 5 23 | echo -n $"Starting $name..." 24 | daemon --pidfile=${pidfile} ${run} 25 | ret=$? 26 | echo 27 | [ $ret -eq 0 ] && touch ${lockfile} 28 | return $ret 29 | } 30 | 31 | stop() { 32 | echo -n $"Stopping $name..." 33 | killproc -p ${pidfile} ${name} 34 | ret=$? 35 | echo 36 | [ $ret -eq 0 ] && rm -f ${lockfile} ${pidfile} 37 | return $ret 38 | } 39 | 40 | restart() { 41 | stop 42 | start 43 | } 44 | 45 | case "$1" in 46 | start) 47 | start 48 | ;; 49 | stop) 50 | stop 51 | ;; 52 | restart) 53 | restart 54 | ;; 55 | *) 56 | echo $"Usage: $name {start|stop|restart}" 57 | exit 1 58 | esac 59 | 60 | exit $? 61 | -------------------------------------------------------------------------------- /log4crc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 8 | 9 | 0 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/common/BaseType.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : BaseType.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : Define types 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _BASE_TYPE_H_ 12 | #define _BASE_TYPE_H_ 13 | 14 | typedef char b1_; 15 | typedef unsigned char ub1_; 16 | typedef short b2_; 17 | typedef unsigned short ub2_; 18 | typedef int b4_; 19 | typedef unsigned int ub4_; 20 | typedef long long b8_; 21 | typedef unsigned long long ub8_; 22 | 23 | typedef float fb4_; 24 | typedef double fb8_; 25 | 26 | typedef char c1_; 27 | 28 | typedef enum { false_v, true_v } bool_; 29 | 30 | #define null_v nullptr 31 | 32 | typedef void none_; 33 | typedef void* obj_; 34 | 35 | #include 36 | 37 | #endif // _BASE_TYPE_H_ 38 | -------------------------------------------------------------------------------- /src/common/CAutoLock.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CAutoLock.cpp 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : helper for auto-locker 8 | ============================================================================ 9 | */ 10 | 11 | #include "CAutoLock.h" 12 | 13 | CAutoLock::CAutoLock(CLock* lock) : _lock(lock) { 14 | if (_lock) { 15 | _lock->lock(); 16 | } 17 | } 18 | 19 | CAutoLock::~CAutoLock() { 20 | if (_lock) { 21 | _lock->unlock(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/common/CAutoLock.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CAutoLock.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : helper for auto-locker 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_AUTO_LOCK_H_ 12 | #define _C_AUTO_LOCK_H_ 13 | 14 | #include "CLock.h" 15 | 16 | class CAutoLock : public CBase { 17 | public: 18 | CAutoLock(CLock* lock = null_v); 19 | virtual ~CAutoLock(); 20 | 21 | const CLock* get() const { return _lock; } 22 | 23 | private: 24 | // Cannot new this class 25 | obj_ operator new(size_t size); 26 | CLock* _lock; 27 | }; 28 | 29 | #endif // _C_AUTO_LOCK_H_ 30 | -------------------------------------------------------------------------------- /src/common/CAutoPtr.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CAutoPtr.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : Encapsulation of auto-pointer 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_AUTO_PTR_H_ 12 | #define _C_AUTO_PTR_H_ 13 | 14 | #include "CBase.h" 15 | 16 | template 17 | class CAutoPtr : public CBase { 18 | public: 19 | explicit CAutoPtr(T* p = null_v) : _p(p) {} 20 | 21 | CAutoPtr(CAutoPtr& ptr) : _p(ptr.release()) {} 22 | 23 | virtual ~CAutoPtr() { delete _p; } 24 | 25 | T* release() { 26 | T* p = _p; 27 | 28 | _p = null_v; 29 | 30 | return p; 31 | } 32 | 33 | const T* get() const { return _p; } 34 | 35 | none_ reset(const T* p = null_v) { 36 | if (p != _p) { 37 | delete _p; 38 | _p = p; 39 | } 40 | } 41 | 42 | CAutoPtr& operator=(CAutoPtr& ptr) { 43 | if (this != &ptr) { 44 | reset(ptr.release()); 45 | } 46 | 47 | return *this; 48 | } 49 | 50 | T& operator*() const { return *_p; } 51 | 52 | T* operator->() const { return _p; } 53 | 54 | private: 55 | // Cannot new this class 56 | obj_ operator new(size_t size); 57 | T* _p; 58 | }; 59 | 60 | #endif // _C_AUTO_PTR_H_ 61 | -------------------------------------------------------------------------------- /src/common/CBase.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CBase.cpp 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : The base class 8 | ============================================================================ 9 | */ 10 | 11 | #include "CBase.h" 12 | 13 | #include 14 | #include 15 | 16 | log4c_category_t* CBase::_category = null_v; 17 | 18 | bool_ CBase::initialize() { 19 | if (0 != log4c_init()) { 20 | return false_v; 21 | } 22 | 23 | _category = log4c_category_get("HFRSLog"); 24 | 25 | return true_v; 26 | } 27 | 28 | none_ CBase::uninitialize() { log4c_fini(); } 29 | 30 | log4c_category_t* CBase::getCategory() { return _category; } 31 | 32 | b4_ CBase::sleep(ub4_ ss, ub4_ mss) { 33 | struct timeval delay; 34 | 35 | delay.tv_sec = ss; 36 | delay.tv_usec = mss * 1000; 37 | 38 | return select(0, null_v, null_v, null_v, &delay); 39 | } 40 | 41 | ub8_ CBase::now() { 42 | timeval tv; 43 | 44 | if (0 != gettimeofday(&tv, null_v)) { 45 | log_fatal("CBase::now: failed to call gettimeofday"); 46 | 47 | return 0; 48 | } 49 | 50 | return tv.tv_sec * 1000000 + tv.tv_usec; 51 | } 52 | 53 | CBase::CBase() {} 54 | 55 | CBase::~CBase() {} 56 | -------------------------------------------------------------------------------- /src/common/CBase.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CBase.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : The base class 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_BASE_H_ 12 | #define _C_BASE_H_ 13 | 14 | #include "BaseType.h" 15 | 16 | extern "C" { 17 | #include 18 | } 19 | 20 | class CBase { 21 | public: 22 | static bool_ initialize(); 23 | static none_ uninitialize(); 24 | 25 | static log4c_category_t *getCategory(); 26 | static b4_ sleep(ub4_ seconds, ub4_ ms = 0); 27 | static ub8_ now(); 28 | 29 | protected: 30 | CBase(); 31 | ~CBase(); 32 | 33 | private: 34 | static log4c_category_t *_category; 35 | }; 36 | 37 | #define log_debug(message, args...) \ 38 | log4c_category_log(CBase::getCategory(), LOG4C_PRIORITY_DEBUG, message, \ 39 | ##args) 40 | #define log_info(message, args...) \ 41 | log4c_category_log(CBase::getCategory(), LOG4C_PRIORITY_INFO, message, \ 42 | ##args) 43 | #define log_notice(message, args...) \ 44 | log4c_category_log(CBase::getCategory(), LOG4C_PRIORITY_NOTICE, message, \ 45 | ##args) 46 | #define log_warn(message, args...) \ 47 | log4c_category_log(CBase::getCategory(), LOG4C_PRIORITY_WARN, message, \ 48 | ##args) 49 | #define log_error(message, args...) \ 50 | log4c_category_log(CBase::getCategory(), LOG4C_PRIORITY_ERROR, message, \ 51 | ##args) 52 | #define log_crit(message, args...) \ 53 | log4c_category_log(CBase::getCategory(), LOG4C_PRIORITY_CRIT, message, \ 54 | ##args) 55 | #define log_alert(message, args...) \ 56 | log4c_category_log(CBase::getCategory(), LOG4C_PRIORITY_ALERT, message, \ 57 | ##args) 58 | #define log_fatal(message, args...) \ 59 | log4c_category_log(CBase::getCategory(), LOG4C_PRIORITY_FATAL, message, \ 60 | ##args) 61 | 62 | #endif // _C_BASE_H_ 63 | -------------------------------------------------------------------------------- /src/common/CCond.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CCond.cpp 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : Encapsulation of condition 8 | ============================================================================ 9 | */ 10 | 11 | #include "CCond.h" 12 | 13 | #include "CMutex.h" 14 | 15 | CCond::CCond(CMutex *mutex) { 16 | assert(mutex); 17 | _mutex = mutex; 18 | 19 | if (0 != pthread_cond_init(&_cond, null_v)) { 20 | log_fatal("CCond::CCond: failed to call pthread_cond_init"); 21 | } 22 | } 23 | 24 | CCond::~CCond() { 25 | if (0 != pthread_cond_destroy(&_cond)) { 26 | log_fatal("CCond::~CCond: failed to call pthread_cond_destroy"); 27 | } 28 | } 29 | 30 | bool_ CCond::lock(bool_ check) { 31 | if (0 != pthread_cond_wait(&_cond, _mutex->getMutex())) { 32 | log_fatal("CCond::Lock: failed to call pthread_cond_wait"); 33 | 34 | return false_v; 35 | } 36 | 37 | return true_v; 38 | } 39 | 40 | none_ CCond::unlock() { 41 | if (0 != pthread_cond_signal(&_cond)) { 42 | log_fatal("CCond::Unlock: failed to call pthread_cond_signal"); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/common/CCond.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CCond.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : Encapsulation of condition 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_COND_H_ 12 | #define _C_COND_H_ 13 | 14 | #include 15 | 16 | #include "CLock.h" 17 | 18 | class CMutex; 19 | 20 | class CCond : public CLock { 21 | public: 22 | CCond(CMutex *mutex); 23 | virtual ~CCond(); 24 | 25 | bool_ lock(bool_ check = false_v); 26 | none_ unlock(); 27 | 28 | private: 29 | pthread_cond_t _cond; 30 | CMutex *_mutex; 31 | }; 32 | 33 | #endif // _C_COND_H_ 34 | -------------------------------------------------------------------------------- /src/common/CConfReader.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CIniReader.cpp 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : ini file reader 8 | ============================================================================ 9 | */ 10 | 11 | #include "CConfReader.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | CConfReader::CConfReader(const c1_ *fileName) { 20 | if (null_v == fileName || 0 == fileName[0]) { 21 | log_fatal("configure file name is not right"); 22 | exit(0); 23 | } 24 | 25 | FILE *file = fopen(fileName, "r"); 26 | 27 | if (null_v == file) { 28 | log_fatal("configure file is not exist"); 29 | exit(0); 30 | } 31 | 32 | ub4_ i = 0; 33 | 34 | do { 35 | _buffer[i] = fgetc(file); 36 | 37 | if ((c1_)EOF == _buffer[i]) { 38 | break; 39 | } else { 40 | i++; 41 | } 42 | } while (i < MAX_CONFIG_FILE_SIZE); 43 | 44 | if (MAX_CONFIG_FILE_SIZE <= i) { 45 | fclose(file); 46 | log_fatal("configure file is too big"); 47 | exit(0); 48 | } 49 | 50 | _bufferSize = i; 51 | fclose(file); 52 | } 53 | 54 | CConfReader::~CConfReader() {} 55 | 56 | b4_ CConfReader::_isNewline(c1_ c) { return ('\n' == c || '\r' == c) ? 1 : 0; } 57 | 58 | b4_ CConfReader::_isEndOfString(c1_ c) { return '\0' == c ? 1 : 0; } 59 | 60 | b4_ CConfReader::_isLeftBarce(c1_ c) { return '[' == c ? 1 : 0; } 61 | 62 | b4_ CConfReader::_isRightBrace(b1_ c) { return ']' == c ? 1 : 0; } 63 | 64 | b4_ CConfReader::_parseFile(const c1_ *section, const c1_ *key, b4_ *secS, 65 | b4_ *secE, b4_ *keyS, b4_ *keyE, b4_ *valueS, 66 | b4_ *valueE) { 67 | b4_ i = 0; 68 | 69 | *secE = *secS = *keyE = *keyS = *valueS = *valueE = -1; 70 | 71 | while (!_isEndOfString(_buffer[i])) { 72 | // find the section 73 | if ((0 == i || _isNewline(_buffer[i - 1])) && 74 | _isLeftBarce(_buffer[i])) { 75 | b4_ section_start = i + 1; 76 | 77 | // find the ']' 78 | do { 79 | i++; 80 | } while (!_isRightBrace(_buffer[i]) && !_isEndOfString(_buffer[i])); 81 | 82 | if (0 == 83 | strncmp(_buffer + section_start, section, i - section_start)) { 84 | i++; 85 | 86 | // Skip over space char after ']' 87 | while (isspace(_buffer[i])) { 88 | i++; 89 | } 90 | 91 | // find the section 92 | *secS = section_start - 1; 93 | *secE = i - 1; 94 | 95 | while ( 96 | !(_isNewline(_buffer[i - 1]) && _isLeftBarce(_buffer[i])) && 97 | !_isEndOfString(_buffer[i])) { 98 | // get a new line 99 | b4_ newlineStart = i; 100 | b4_ j = i; 101 | 102 | while (!_isNewline(_buffer[i]) && 103 | !_isEndOfString(_buffer[i])) { 104 | i++; 105 | } 106 | 107 | // now i is equal to the end of the line 108 | if (';' != _buffer[j]) { 109 | while (j < i && '=' != _buffer[j]) { 110 | j++; 111 | 112 | if ('=' == _buffer[j]) { 113 | if (!strncmp(key, _buffer + newlineStart, 114 | j - newlineStart)) { 115 | // find the key ok 116 | *keyS = newlineStart; 117 | *keyE = j - 1; 118 | 119 | *valueS = j + 1; 120 | *valueE = i; 121 | 122 | return 0; 123 | } 124 | } 125 | } 126 | } 127 | 128 | i++; 129 | } 130 | } 131 | } else { 132 | i++; 133 | } 134 | } 135 | 136 | return 1; 137 | } 138 | 139 | b4_ CConfReader::readString(const c1_ *section, const c1_ *key, c1_ *value, 140 | ub4_ size) { 141 | if (null_v == section || 0 == section[0] || null_v == key || 0 == key[0] || 142 | null_v == value || 0 == size) { 143 | return 1; 144 | } 145 | 146 | if (0 == _bufferSize) { 147 | return 2; 148 | } 149 | 150 | b4_ secS, secE, keyS, keyE, valueS, valueE; 151 | 152 | if (!_parseFile(section, key, &secS, &secE, &keyS, &keyE, &valueS, 153 | &valueE)) { 154 | ub4_ length = valueE - valueS; 155 | 156 | if (size - 1 < length) { 157 | length = size - 1; 158 | } 159 | 160 | memcpy(value, _buffer + valueS, length); 161 | value[length] = 0; 162 | } else { 163 | return 3; 164 | } 165 | 166 | return 0; 167 | } 168 | 169 | b4_ CConfReader::readByte(const c1_ *section, const c1_ *key, ub1_ *value) { 170 | c1_ val[32] = {0}; 171 | b4_ ret = readString(section, key, val, 32); 172 | 173 | if (0 != ret) { 174 | return ret; 175 | } 176 | 177 | *value = (ub1_)atoi(val); 178 | 179 | return 0; 180 | } 181 | 182 | b4_ CConfReader::readShort(const c1_ *section, const c1_ *key, ub2_ *value) { 183 | c1_ val[32] = {0}; 184 | b4_ ret = readString(section, key, val, 32); 185 | 186 | if (0 != ret) { 187 | return ret; 188 | } 189 | 190 | *value = (ub2_)atoi(val); 191 | 192 | return 0; 193 | } 194 | 195 | b4_ CConfReader::readInt(const c1_ *section, const c1_ *key, ub4_ *value) { 196 | c1_ val[32] = {0}; 197 | b4_ ret = readString(section, key, val, 32); 198 | 199 | if (0 != ret) { 200 | return ret; 201 | } 202 | 203 | *value = atoi(val); 204 | 205 | return 0; 206 | } 207 | -------------------------------------------------------------------------------- /src/common/CConfReader.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CConfReader.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : configure file reader 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_CONF_READER_H_ 12 | #define _C_CONF_READER_H_ 13 | 14 | #include "CBase.h" 15 | 16 | const ub4_ MAX_CONFIG_FILE_SIZE = 0x8000; 17 | 18 | class CConfReader : CBase { 19 | public: 20 | CConfReader(const c1_ *fileName); 21 | virtual ~CConfReader(); 22 | 23 | b4_ readString(const c1_ *section, const c1_ *key, c1_ *value, ub4_ size); 24 | b4_ readByte(const c1_ *section, const c1_ *key, ub1_ *value); 25 | b4_ readShort(const c1_ *section, const c1_ *key, ub2_ *value); 26 | b4_ readInt(const c1_ *section, const c1_ *key, ub4_ *value); 27 | 28 | private: 29 | b4_ _isNewline(c1_ c); 30 | b4_ _isEndOfString(c1_ c); 31 | b4_ _isLeftBarce(c1_ c); 32 | b4_ _isRightBrace(c1_ c); 33 | b4_ _parseFile(const c1_ *section, const c1_ *key, b4_ *secS, b4_ *secE, 34 | b4_ *keyS, b4_ *keyE, b4_ *valueS, b4_ *valueE); 35 | 36 | c1_ _buffer[MAX_CONFIG_FILE_SIZE]; 37 | b4_ _bufferSize; 38 | }; 39 | 40 | #endif // _C_CONF_READER_H_ 41 | -------------------------------------------------------------------------------- /src/common/CLock.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CLock 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : The protocol of lockers 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_LOCK_H_ 12 | #define _C_LOCK_H_ 13 | 14 | #include "CBase.h" 15 | 16 | class CLock : public CBase { 17 | public: 18 | virtual bool_ lock(bool_ check = false_v) = 0; 19 | virtual none_ unlock() = 0; 20 | 21 | protected: 22 | CLock() {} 23 | 24 | virtual ~CLock() {} 25 | }; 26 | 27 | #endif // _C_LOCK_H_ 28 | -------------------------------------------------------------------------------- /src/common/CLoopBuffer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CLoopBuffer.cpp 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : Encapsulation of loop buffer 8 | ============================================================================ 9 | */ 10 | 11 | #include "CLoopBuffer.h" 12 | 13 | #include 14 | 15 | #include "CAutoLock.h" 16 | #include "CMutex.h" 17 | 18 | CLoopBuffer::CLoopBuffer(ub4_ size, CMutex *mutex, bool_ isPadding) 19 | : _totalSize(size) { 20 | assert(size); 21 | _actualSize = size; 22 | _usedSize = 0; 23 | _freeSize = size; 24 | _mutex = mutex; 25 | _isPadding = isPadding; 26 | _padding = 0; 27 | _writePos = _readPos = __buffer = new ub1_[size]; 28 | assert(__buffer); 29 | } 30 | 31 | CLoopBuffer::~CLoopBuffer() { delete[] __buffer; } 32 | 33 | bool_ CLoopBuffer::write(const ub1_ *buffer, ub4_ size) { 34 | assert(buffer); 35 | assert(size); 36 | 37 | CAutoLock al(_mutex); 38 | 39 | if (size > _freeSize) { 40 | return false_v; 41 | } 42 | 43 | if (_writePos >= _readPos) { 44 | ub4_ rightSize = __buffer + _totalSize - _writePos; 45 | ub4_ leftSize = _readPos - __buffer; 46 | 47 | assert(_padding == 0); 48 | assert(_actualSize == _totalSize); 49 | assert(_freeSize <= _totalSize); 50 | assert(_writePos - _readPos == (b4_)_usedSize); 51 | assert(rightSize + leftSize == _freeSize); 52 | 53 | if (rightSize >= size) { 54 | memcpy(_writePos, buffer, size); 55 | _writePos += size; 56 | } else if (_isPadding) { 57 | if (size > leftSize) { 58 | return false_v; 59 | } 60 | 61 | _padding = rightSize; 62 | _actualSize -= _padding; 63 | _freeSize -= _padding; 64 | assert(_freeSize == leftSize); 65 | 66 | memcpy(__buffer, buffer, size); 67 | _writePos = __buffer + size; 68 | } else { 69 | memcpy(_writePos, buffer, rightSize); 70 | memcpy(__buffer, buffer + rightSize, size - rightSize); 71 | _writePos = __buffer + size - rightSize; 72 | } 73 | } else { 74 | assert(_readPos - _writePos == (b4_)_freeSize); 75 | memcpy(_writePos, buffer, size); 76 | _writePos += size; 77 | assert(_writePos <= _readPos); 78 | } 79 | 80 | _usedSize += size; 81 | _freeSize -= size; 82 | 83 | return true_v; 84 | } 85 | 86 | ub4_ CLoopBuffer::read(ub1_ *buffer, ub4_ size) { 87 | assert(buffer); 88 | assert(size); 89 | 90 | CAutoLock al(_mutex); 91 | 92 | if (0 == _usedSize) { 93 | return 0; 94 | } 95 | 96 | if (_writePos > _readPos) { 97 | assert(_padding == 0); 98 | assert(_writePos - _readPos == (b4_)_usedSize); 99 | 100 | if (size > _usedSize) { 101 | size = _usedSize; 102 | } 103 | 104 | memcpy(buffer, _readPos, size); 105 | _readPos += size; 106 | } else { 107 | assert(_readPos - _writePos == (b4_)_freeSize); 108 | 109 | ub4_ uiRightSize = __buffer + _actualSize - _readPos; 110 | ub4_ uiLeftSize = _writePos - __buffer; 111 | 112 | assert(_actualSize == _totalSize - _padding); 113 | assert(uiRightSize + uiLeftSize == _usedSize); 114 | 115 | if (uiRightSize >= size) { 116 | memcpy(buffer, _readPos, size); 117 | _readPos += size; 118 | 119 | if (uiRightSize == size && _padding) { 120 | _actualSize += _padding; 121 | _freeSize += _padding; 122 | _padding = 0; 123 | _readPos = __buffer; 124 | } 125 | } else { 126 | if (_usedSize < size) { 127 | size = _usedSize; 128 | } 129 | 130 | memcpy(buffer, _readPos, uiRightSize); 131 | memcpy(buffer + uiRightSize, __buffer, size - uiRightSize); 132 | _readPos = __buffer + size - uiRightSize; 133 | 134 | if (_padding) { 135 | _actualSize += _padding; 136 | _freeSize += _padding; 137 | _padding = 0; 138 | } 139 | } 140 | } 141 | 142 | _usedSize -= size; 143 | _freeSize += size; 144 | 145 | return size; 146 | } 147 | 148 | none_ CLoopBuffer::reset() { 149 | CAutoLock al(_mutex); 150 | 151 | _actualSize = _totalSize; 152 | _usedSize = 0; 153 | _freeSize = _totalSize; 154 | _padding = 0; 155 | _writePos = _readPos = __buffer; 156 | } 157 | -------------------------------------------------------------------------------- /src/common/CLoopBuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CLoopBuffer.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : Encapsulation of loop buffer 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_LOOP_BUFFER_H_ 12 | #define _C_LOOP_BUFFER_H_ 13 | 14 | #include "CBase.h" 15 | 16 | class CMutex; 17 | 18 | class CLoopBuffer : public CBase { 19 | public: 20 | CLoopBuffer(ub4_ size, CMutex *mutex = null_v, bool_ isPadding = false_v); 21 | virtual ~CLoopBuffer(); 22 | 23 | // buffer: the buffer to be write to the LoopBuffer 24 | // size: the buffer size 25 | // Return true_v if success, otherwise false_v 26 | bool_ write(const ub1_ *buffer, ub4_ size); 27 | 28 | // buffer: the buffer to be read in 29 | // size: the buffer size 30 | // Return actual read size 31 | ub4_ read(ub1_ *buffer, ub4_ size); 32 | 33 | none_ reset(); 34 | 35 | ub4_ getTotalSize() const { return _totalSize; } 36 | 37 | ub4_ getActualSize() const { return _actualSize; } 38 | 39 | ub4_ getFreeSize() const { return _freeSize; } 40 | 41 | ub4_ getUsedSize() const { return _usedSize; } 42 | 43 | protected: 44 | ub1_ *__buffer; 45 | 46 | private: 47 | const ub4_ _totalSize; 48 | ub4_ _actualSize; // _totalSize - _padding 49 | ub4_ _usedSize; // _actualSize - _freeSize 50 | ub4_ _freeSize; // _actualSize - _userSize 51 | 52 | bool_ _isPadding; 53 | ub4_ _padding; // = _totalSize - _actualSize 54 | 55 | ub1_ *_writePos; 56 | ub1_ *_readPos; 57 | 58 | CMutex *_mutex; 59 | }; 60 | 61 | #endif // _C_LOOP_BUFFER_H_ 62 | -------------------------------------------------------------------------------- /src/common/CMutex.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CMutex.cpp 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : Encapsulation of mutex 8 | ============================================================================ 9 | */ 10 | 11 | #include "CMutex.h" 12 | 13 | #include 14 | 15 | CMutex::CMutex(bool_ recursive) { 16 | pthread_mutexattr_t attr; 17 | 18 | pthread_mutexattr_init(&attr); 19 | 20 | if (recursive) { 21 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); 22 | } 23 | 24 | if (0 != pthread_mutex_init(&_mutex, &attr)) { 25 | log_fatal("CMutex::CMutex: failed to call pthread_mutex_init"); 26 | } 27 | 28 | pthread_mutexattr_destroy(&attr); 29 | } 30 | 31 | CMutex::~CMutex() { 32 | if (0 != pthread_mutex_destroy(&_mutex)) { 33 | log_fatal("CMutex::~CMutex: failed to call pthread_mutex_destroy"); 34 | } 35 | } 36 | 37 | bool_ CMutex::lock(bool_ check) { 38 | b4_ ret = 0; 39 | 40 | if (check) { 41 | ret = pthread_mutex_trylock(&_mutex); 42 | } else { 43 | ret = pthread_mutex_lock(&_mutex); 44 | } 45 | 46 | if (EBUSY == ret && check) { 47 | return false_v; 48 | } 49 | 50 | if (0 != ret) { 51 | if (check) { 52 | log_fatal("CMutex::Lock: failed to call pthread_mutext_trylock"); 53 | } else { 54 | log_fatal("CMutex::Lock: failed to call pthread_mutext_lock"); 55 | } 56 | 57 | return false_v; 58 | } 59 | 60 | return true_v; 61 | } 62 | 63 | none_ CMutex::unlock() { 64 | if (0 != pthread_mutex_unlock(&_mutex)) { 65 | log_fatal("CMutex::Unlock: failed to call pthread_mutex_unlock"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/common/CMutex.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CMutex.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : Encapsulation of mutex 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_MUTEX_H_ 12 | #define _C_MUTEX_H_ 13 | 14 | #include 15 | 16 | #include "CLock.h" 17 | 18 | class CMutex : public CLock { 19 | public: 20 | CMutex(bool_ recursive = false_v); 21 | virtual ~CMutex(); 22 | 23 | bool_ lock(bool_ check = false_v); 24 | none_ unlock(); 25 | 26 | pthread_mutex_t *getMutex() { return &_mutex; } 27 | 28 | private: 29 | pthread_mutex_t _mutex; 30 | }; 31 | 32 | #endif // _C_MUTEX_H_ 33 | -------------------------------------------------------------------------------- /src/common/CResource.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CResource.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : Encapsulation of resource management 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_RESOURCE_H_ 12 | #define _C_RESOURCE_H_ 13 | 14 | #include 15 | 16 | #include "CAutoLock.h" 17 | #include "CBase.h" 18 | 19 | class CMutex; 20 | 21 | template 22 | class CResource : public CBase { 23 | public: 24 | CResource(const ub4_ maxNum, CMutex *mutex = null_v) : _maxNum(maxNum) { 25 | assert(_maxNum > 1); 26 | _unitGroup = (T **)new b1_[_maxNum * sizeof(T *)]; 27 | _mutex = mutex; 28 | 29 | for (ub4_ ui = 0; ui < _maxNum; ui++) { 30 | _unitGroup[ui] = new T(); 31 | _freeDeque.push_back(_unitGroup[ui]); 32 | } 33 | } 34 | 35 | CResource(const ub4_ maxNum, Y *container, CMutex *mutex = null_v) 36 | : _maxNum(maxNum) { 37 | assert(_maxNum > 1); 38 | assert(null_v != container); 39 | _unitGroup = (T **)new b1_[_maxNum * sizeof(T *)]; 40 | _mutex = mutex; 41 | 42 | for (ub4_ ui = 0; ui < _maxNum; ui++) { 43 | _unitGroup[ui] = new T(container); 44 | _freeDeque.push_back(_unitGroup[ui]); 45 | } 46 | } 47 | 48 | virtual ~CResource() { 49 | _freeDeque.clear(); 50 | 51 | for (ub4_ ui = 0; ui < _maxNum; ui++) { 52 | delete _unitGroup[ui]; 53 | } 54 | 55 | delete[] _unitGroup; 56 | } 57 | 58 | T *allocate() { 59 | CAutoLock al(_mutex); 60 | 61 | if (0 < _freeDeque.size()) { 62 | T *pUnit = _freeDeque.front(); 63 | 64 | assert(pUnit); 65 | _freeDeque.pop_front(); 66 | 67 | return pUnit; 68 | } 69 | 70 | return null_v; 71 | } 72 | 73 | bool_ reclaim(T *&unit) { 74 | assert(unit); 75 | CAutoLock al(_mutex); 76 | 77 | ub4_ ui = 0; 78 | 79 | for (; ui < _maxNum; ui++) { 80 | if (unit == _unitGroup[ui]) { 81 | break; 82 | } 83 | } 84 | 85 | if (ui != _maxNum) { 86 | _freeDeque.push_back(unit); 87 | unit = null_v; 88 | 89 | return true_v; 90 | } 91 | 92 | return false_v; 93 | } 94 | 95 | ub4_ size() const { return (ub4_)_freeDeque.size(); } 96 | 97 | private: 98 | const ub4_ _maxNum; 99 | T **_unitGroup; 100 | CMutex *_mutex; 101 | 102 | std::deque _freeDeque; 103 | }; 104 | 105 | #endif // _C_RESOURCE_H_ 106 | -------------------------------------------------------------------------------- /src/common/CSem.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CSem.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : Encapsulation of semaphore 8 | ============================================================================ 9 | */ 10 | 11 | #include "CSem.h" 12 | 13 | #include 14 | 15 | CSem::CSem(ub4_ value) { 16 | if (-1 == sem_init(&_sem, 0, value)) 17 | log_fatal("CSem::CSem: failed to call sem_init"); 18 | } 19 | 20 | CSem::~CSem() { 21 | if (-1 == sem_destroy(&_sem)) 22 | log_fatal("CSem::~CSem: failed to call sem_destroy"); 23 | } 24 | 25 | bool_ CSem::lock(bool_ check) { 26 | b4_ iRet = 0; 27 | 28 | if (check) { 29 | iRet = sem_trywait(&_sem); 30 | } else { 31 | iRet = sem_wait(&_sem); 32 | } 33 | 34 | if (-1 == iRet) { 35 | if (EBUSY == errno && check) { 36 | return false_v; 37 | } 38 | 39 | if (check) { 40 | log_fatal("CSem::Lock: failed to call sem_trywait"); 41 | } else { 42 | log_fatal("CSem::Lock: failed to call sem_wait"); 43 | } 44 | 45 | return false_v; 46 | } 47 | 48 | return true_v; 49 | } 50 | 51 | none_ CSem::unlock() { 52 | if (-1 == sem_post(&_sem)) { 53 | log_fatal("CSem::Unlock: failed to call sem_post"); 54 | } 55 | } 56 | 57 | ub4_ CSem::getValue() { 58 | b4_ n = 0; 59 | 60 | if (-1 == sem_getvalue(&_sem, &n)) { 61 | log_fatal("CSem::GetValue: failed to call sem_getvalue"); 62 | } 63 | 64 | return (ub4_)n; 65 | } 66 | -------------------------------------------------------------------------------- /src/common/CSem.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CSem.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : Encapsulation of semaphore 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_SEM_H_ 12 | #define _C_SEM_H_ 13 | 14 | #include 15 | 16 | #include "CLock.h" 17 | 18 | class CSem : public CLock { 19 | public: 20 | CSem(ub4_ value = 1); 21 | virtual ~CSem(); 22 | 23 | bool_ lock(bool_ check = false_v); 24 | none_ unlock(); 25 | 26 | ub4_ getValue(); 27 | 28 | private: 29 | sem_t _sem; 30 | }; 31 | 32 | #endif // _C_SEM_H_ 33 | -------------------------------------------------------------------------------- /src/common/CTimerManager.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CTimerManager.cpp 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : Encapsulation of timer manager 8 | ============================================================================ 9 | */ 10 | 11 | #include "CTimerManager.h" 12 | 13 | #include 14 | 15 | #include "CAutoLock.h" 16 | 17 | CTimerManager::CTimerManager(ub4_ maxTimerNum, ub4_ threadStackSize) 18 | : _worker(threadStackSize), 19 | _mutex(true_v), 20 | _timerRes(maxTimerNum, &_mutex) { 21 | _timerList = null_v; 22 | _lastTimer = null_v; 23 | _curTimer = null_v; 24 | 25 | _worker.work(this, true_v); 26 | } 27 | 28 | CTimerManager::~CTimerManager() {} 29 | 30 | ub8_ CTimerManager::setTimer(ub4_ period, obj_ parameterI, obj_ parameterII, 31 | ub4_ times) { 32 | TTimer *timer = _timerRes.allocate(); 33 | 34 | if (null_v == timer) { 35 | log_crit("CTimerManager::setTimer: no more timers can be allocated"); 36 | 37 | return 0; 38 | } 39 | 40 | assert(ETimerStatus::NOTHING == timer->status || 41 | ETimerStatus::DELETED == timer->status); 42 | struct timeval curTime; 43 | gettimeofday(&curTime, null_v); 44 | 45 | timer->period = period; 46 | timer->parameterI = parameterI; 47 | timer->parameterII = parameterII; 48 | timer->times = times; 49 | timer->baseS = curTime.tv_sec; 50 | timer->baseUS = curTime.tv_usec; 51 | timer->previous = null_v; 52 | timer->next = null_v; 53 | timer->status = ETimerStatus::TO_BE_ADD; 54 | 55 | _mutex.lock(); 56 | _queueForAdd.push(timer); 57 | _mutex.unlock(); 58 | 59 | log_debug("CTimerManager::setTimer: time id-%p, period-%uSec, times-%u", 60 | timer, period, times); 61 | 62 | return (ub8_)timer; 63 | } 64 | 65 | none_ CTimerManager::killTimer(ub8_ timerId) { 66 | assert(timerId > 0); 67 | if (0 == timerId) { 68 | return; 69 | } 70 | 71 | TTimer *timer = (TTimer *)timerId; 72 | assert(ETimerStatus::NOTHING != timer->status); 73 | CAutoLock al(&_mutex); 74 | 75 | if (ETimerStatus::TO_BE_ADD == timer->status) { 76 | timer->status = ETimerStatus::DELETED; 77 | _timerRes.reclaim(timer); 78 | 79 | return; 80 | } 81 | 82 | if (ETimerStatus::ADDED != timer->status) { 83 | return; // The timer will be deleted or has been deleted 84 | } 85 | 86 | timer->status = ETimerStatus::TO_BE_DEL; 87 | _queueForDel.push(timer); 88 | 89 | log_debug("CTimerManager::killTimer: time id-%p.", timer); 90 | } 91 | 92 | bool_ CTimerManager::working() { 93 | _mutex.lock(); 94 | 95 | while (!_queueForAdd.empty()) { 96 | TTimer *timer = _queueForAdd.front(); 97 | 98 | if (ETimerStatus::TO_BE_ADD == timer->status) { 99 | _addTimer(timer); 100 | } 101 | 102 | _queueForAdd.pop(); 103 | } 104 | 105 | while (!_queueForDel.empty()) { 106 | TTimer *pTimer = _queueForDel.front(); 107 | 108 | if (ETimerStatus::TO_BE_DEL == pTimer->status) { 109 | if (pTimer == _curTimer) { 110 | _curTimer = _curTimer->next; 111 | } 112 | 113 | _delTimer(pTimer); 114 | } 115 | 116 | _queueForDel.pop(); 117 | } 118 | 119 | _mutex.unlock(); 120 | 121 | struct timeval curTime; 122 | gettimeofday(&curTime, null_v); 123 | 124 | if (null_v == _curTimer) { 125 | if (null_v == _timerList) { 126 | // Sleep for 0.1s 127 | sleep(0, 100); 128 | 129 | return true_v; 130 | } 131 | 132 | _curTimer = _timerList; 133 | } 134 | 135 | b4_ i = _curTimer->period + _curTimer->baseS; 136 | 137 | if ((i < curTime.tv_sec) || 138 | (i == curTime.tv_sec && _curTimer->baseUS <= curTime.tv_usec)) { 139 | if (__onTimer((ub8_)_curTimer, _curTimer->parameterI, 140 | _curTimer->parameterII)) { 141 | _curTimer->baseS = curTime.tv_sec; 142 | _curTimer->baseUS = curTime.tv_usec; 143 | 144 | if (0 != _curTimer->times) { 145 | _curTimer->times--; 146 | 147 | if (0 == _curTimer->times) { 148 | TTimer *pCurTimer = _curTimer; 149 | 150 | _curTimer = _curTimer->next; 151 | _delTimer(pCurTimer); 152 | 153 | return true_v; 154 | } 155 | } 156 | } else { 157 | TTimer *pCurTimer = _curTimer; 158 | 159 | _curTimer = _curTimer->next; 160 | _delTimer(pCurTimer); 161 | 162 | return true_v; 163 | } 164 | } 165 | 166 | _curTimer = _curTimer->next; 167 | 168 | return true_v; 169 | } 170 | 171 | none_ CTimerManager::_addTimer(TTimer *timer) { 172 | assert(null_v != timer); 173 | 174 | if (null_v == _timerList) { 175 | assert(null_v == _lastTimer); 176 | _lastTimer = _timerList = timer; 177 | } else { 178 | timer->previous = _lastTimer; 179 | _lastTimer->next = timer; 180 | _lastTimer = timer; 181 | } 182 | 183 | timer->status = ETimerStatus::ADDED; 184 | } 185 | 186 | none_ CTimerManager::_delTimer(TTimer *timer) { 187 | assert(null_v != timer); 188 | 189 | if (null_v == timer->previous) { 190 | assert(timer == _timerList); 191 | 192 | if (null_v == timer->next) { 193 | assert(timer == _lastTimer); 194 | _lastTimer = _timerList = null_v; 195 | } else { 196 | _timerList = timer->next; 197 | _timerList->previous = null_v; 198 | } 199 | } else { 200 | if (null_v == timer->next) { 201 | assert(timer == _lastTimer); 202 | _lastTimer = timer->previous; 203 | _lastTimer->next = null_v; 204 | } else { 205 | timer->previous->next = timer->next; 206 | timer->next->previous = timer->previous; 207 | } 208 | } 209 | 210 | timer->status = ETimerStatus::DELETED; 211 | _timerRes.reclaim(timer); 212 | } 213 | -------------------------------------------------------------------------------- /src/common/CTimerManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CTimerManager.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : Encapsulation of timer manager 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_TIMERMANAGER_H_ 12 | #define _C_TIMERMANAGER_H_ 13 | 14 | #include "CBase.h" 15 | #include "CMutex.h" 16 | #include "CResource.h" 17 | #include "CWorker.h" 18 | #include "IWorkable.h" 19 | 20 | enum class ETimerStatus { 21 | NOTHING = 0x0000, 22 | 23 | TO_BE_ADD = 0x0001, 24 | ADDED = 0x0002, 25 | TO_BE_DEL = 0x0004, 26 | DELETED = 0x0008, 27 | }; 28 | 29 | struct TTimer { 30 | ub4_ period; // seconds 31 | obj_ parameterI; 32 | obj_ parameterII; 33 | ub4_ times; 34 | 35 | b4_ baseS; 36 | b4_ baseUS; 37 | 38 | TTimer* previous; 39 | TTimer* next; 40 | 41 | ETimerStatus status; 42 | }; 43 | 44 | #include 45 | 46 | typedef std::queue OperatorQueue; 47 | 48 | // A timer manager, which can accept 1 second as the minimal unit 49 | class CTimerManager : public CBase, public IWorkable { 50 | public: 51 | CTimerManager(ub4_ maxTimerNum, ub4_ threadStackSize); 52 | virtual ~CTimerManager(); 53 | 54 | // timers == 0: Infinite 55 | // return timer id or 0: failed 56 | ub8_ setTimer(ub4_ period, obj_ parameterI, obj_ parameterII, 57 | ub4_ times = 1); 58 | none_ killTimer(ub8_ timerId); 59 | 60 | virtual bool_ working(); 61 | 62 | protected: 63 | virtual bool_ __onTimer(ub8_ timerId, obj_ parameterI, 64 | obj_ parameterII) = 0; 65 | 66 | private: 67 | none_ _addTimer(TTimer* timer); 68 | none_ _delTimer(TTimer* timer); 69 | 70 | CWorker _worker; 71 | CMutex _mutex; 72 | 73 | CResource _timerRes; 74 | TTimer* _timerList; 75 | TTimer* _lastTimer; 76 | 77 | OperatorQueue _queueForAdd; 78 | OperatorQueue _queueForDel; 79 | TTimer* _curTimer; 80 | }; 81 | 82 | #endif // _C_TIMER_MANAGER_H_ 83 | -------------------------------------------------------------------------------- /src/common/CWorker.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CWorkable.cpp 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : Encapsulation of pthread 8 | ============================================================================ 9 | */ 10 | 11 | #include "CWorker.h" 12 | 13 | #include "CAutoLock.h" 14 | #include "IWorkable.h" 15 | 16 | CMutex CWorker::_mutexWorker; 17 | ub4_ CWorker::_workerNum = 0; 18 | bool_ CWorker::_workingCondition = true_v; 19 | 20 | CWorker::CWorker(ub4_ threadStackSize) : _condInformed(&_mutexInformed) { 21 | _threadStackSize = threadStackSize * 1024; 22 | _handle = 0; 23 | _informed = false_v; 24 | _workable = null_v; 25 | } 26 | 27 | CWorker::~CWorker() {} 28 | 29 | none_ CWorker::work(IWorkable *workable, bool_ informed, bool_ sync) { 30 | if (_handle) { 31 | return; 32 | } 33 | 34 | assert(null_v != workable); 35 | _workable = workable; 36 | 37 | if (!informed) { 38 | createThread(); 39 | } else { 40 | CAutoLock al(&_mutexInformed); 41 | 42 | _informed = informed; 43 | 44 | if (false_v == createThread()) { 45 | return; 46 | } 47 | 48 | if (_informed) { 49 | _condInformed.lock(); 50 | } 51 | 52 | if (sync) { 53 | pthread_join(_handle, null_v); 54 | } 55 | } 56 | } 57 | 58 | obj_ CWorker::_run(obj_ object) { 59 | CWorker *worker = (CWorker *)object; 60 | 61 | _mutexWorker.lock(); 62 | _workerNum++; 63 | log_debug("Worker(%016lu) started, totally %d workers ", pthread_self(), 64 | _workerNum); 65 | _mutexWorker.unlock(); 66 | 67 | worker->_mutexInformed.lock(); 68 | 69 | if (worker->_informed) { 70 | worker->_informed = false_v; 71 | worker->_condInformed.unlock(); 72 | } 73 | 74 | worker->_mutexInformed.unlock(); 75 | 76 | while (_workingCondition) { 77 | if (!worker->_workable->working()) { 78 | break; 79 | } 80 | } 81 | 82 | _mutexWorker.lock(); 83 | _workerNum--; 84 | log_debug("Worker(%016lu) ended, totally %d workers", pthread_self(), 85 | _workerNum); 86 | _mutexWorker.unlock(); 87 | 88 | worker->_handle = 0; 89 | 90 | return null_v; 91 | } 92 | 93 | bool_ CWorker::createThread() { 94 | pthread_attr_t attr; 95 | 96 | if (0 != pthread_attr_init(&attr)) { 97 | log_fatal("CWorker::createThread: failed to call pthread_attr_init"); 98 | 99 | return false_v; 100 | } 101 | 102 | if (0 != pthread_attr_setstacksize(&attr, _threadStackSize)) { 103 | log_fatal( 104 | "CWorker::createThread: failed to call " 105 | "pthread_attr_setstacksize"); 106 | 107 | return false_v; 108 | } 109 | 110 | if (0 != pthread_create(&_handle, &attr, CWorker::_run, (obj_)this)) { 111 | log_fatal("CWorker::createThread: failed to call pthread_create"); 112 | 113 | return false_v; 114 | } 115 | 116 | pthread_attr_destroy(&attr); 117 | 118 | return true_v; 119 | } 120 | -------------------------------------------------------------------------------- /src/common/CWorker.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CWorkable.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : Encapsulation of pthread 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_WORKER_H_ 12 | #define _C_WORKER_H_ 13 | 14 | #include "CBase.h" 15 | #include "CCond.h" 16 | #include "CMutex.h" 17 | 18 | class IWorkable; 19 | 20 | class CWorker : public CBase { 21 | public: 22 | CWorker(ub4_ threadStackSize /* k */); 23 | virtual ~CWorker(); 24 | 25 | // sync is invalid unless bInformed = true_v 26 | none_ work(IWorkable* workable, bool_ informed = false_v, 27 | bool_ sync = false_v); 28 | 29 | static none_ stop() { _workingCondition = false_v; } 30 | 31 | static ub4_ getTotalNum() { return _workerNum; } 32 | 33 | private: 34 | static obj_ _run(obj_ object); 35 | 36 | static CMutex _mutexWorker; 37 | static ub4_ _workerNum; 38 | static bool_ _workingCondition; 39 | 40 | bool_ createThread(); 41 | 42 | ub4_ _threadStackSize; // k 43 | pthread_t _handle; 44 | bool_ _informed; 45 | CMutex _mutexInformed; 46 | CCond _condInformed; 47 | 48 | IWorkable* _workable; 49 | }; 50 | 51 | #endif // _C_WORKER_H_ 52 | -------------------------------------------------------------------------------- /src/common/IWorkable.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : IWorkable.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : The protocol of CWorker, used by CWorker instances 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _I_WORKABLE_H_ 12 | #define _I_WORKABLE_H_ 13 | 14 | #include "BaseType.h" 15 | 16 | class IWorkable { 17 | public: 18 | virtual ~IWorkable() {} // to anone_ waring 19 | 20 | virtual bool_ working() = 0; 21 | }; 22 | 23 | #endif // _I_WORKABLE_H_ 24 | -------------------------------------------------------------------------------- /src/common/SConscript: -------------------------------------------------------------------------------- 1 | env = Environment() 2 | env.Append(CCFLAGS = '-std=c++11') 3 | 4 | objs = env.Object(Glob('*.cpp')) 5 | Return('objs') -------------------------------------------------------------------------------- /src/config/Config.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : Config.cpp 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #define GLOBAL_CONFIG 12 | #include "Config.h" 13 | 14 | #include 15 | 16 | #include "../common/CConfReader.h" 17 | #include "DefaultConfig.h" 18 | 19 | namespace Config { 20 | 21 | namespace App { 22 | 23 | c1_ CONF_FILE[Size::PATH]; 24 | ub4_ RUN_AS_DAEMON; 25 | c1_ NAME[Size::NAME]; 26 | ub4_ BASE_BUILD; 27 | ub2_ PROTOCOL_VERSION; 28 | c1_ LISTEN_IP[Size::IP_V4]; 29 | ub2_ LISTEN_PORT; 30 | ub4_ EPOLL_WAIT_EVENT_NUM; 31 | ub4_ NODE_GROUP_NUM; 32 | ub4_ NODE_GROUP_SIZE; 33 | ub4_ TOTAL_SUPPORT_USER_NUM; 34 | ub4_ TOTAL_THREAD_NUM; 35 | ub4_ MESSAGE_MAX_NUM_IN_QUEUE; 36 | ub4_ THREAD_STACK_SIZE; 37 | ub4_ HEARTBEAT_INTERVAL; 38 | 39 | } // namespace App 40 | 41 | namespace Msg { 42 | 43 | ub8_ VALID_DURATION; 44 | ub1_ VALID_NUMBER; 45 | 46 | } // namespace Msg 47 | 48 | namespace Redis { 49 | 50 | c1_ HOST[Size::URL]; 51 | ub2_ PORT; 52 | ub4_ TIMEOUT; 53 | c1_ AUTH[Size::PASSWORD]; 54 | ub1_ DB; 55 | 56 | } // namespace Redis 57 | 58 | none_ initializeApp(CConfReader *conf); 59 | none_ initializeMsg(CConfReader *conf); 60 | none_ initializeRedis(CConfReader *conf); 61 | 62 | none_ initialize(const c1_ *confFileName) { 63 | if (null_v != confFileName && 0 < strlen(confFileName) && 64 | Size::PATH > strlen(confFileName)) { 65 | strncpy(App::CONF_FILE, confFileName, Size::PATH); 66 | } else { 67 | strncpy(App::CONF_FILE, DefaultConfig::App::CONF_FILE, Size::PATH); 68 | } 69 | 70 | CConfReader *conf = new CConfReader(App::CONF_FILE); 71 | 72 | initializeApp(conf); 73 | initializeMsg(conf); 74 | initializeRedis(conf); 75 | } 76 | 77 | none_ initializeApp(CConfReader *conf) { 78 | assert(null_v != conf); 79 | const c1_ *section = "app"; 80 | 81 | if (conf->readInt(section, "run_as_daemon", &App::RUN_AS_DAEMON) || 82 | 0 == App::RUN_AS_DAEMON) { 83 | App::RUN_AS_DAEMON = DefaultConfig::App::RUN_AS_DAEMON; 84 | } 85 | 86 | if (conf->readString(section, "name", App::NAME, Size::NAME) || 87 | 0 == App::NAME[0]) { 88 | strncpy(App::NAME, DefaultConfig::App::NAME, Size::NAME); 89 | } 90 | 91 | if (conf->readInt(section, "base_build", &App::BASE_BUILD) || 92 | 0 == App::BASE_BUILD) { 93 | App::BASE_BUILD = DefaultConfig::App::BASE_BUILD; 94 | } 95 | 96 | if (conf->readShort(section, "protocol_version", 97 | &App::PROTOCOL_VERSION) || 98 | 0 == App::PROTOCOL_VERSION) { 99 | App::PROTOCOL_VERSION = DefaultConfig::App::PROTOCOL_VERSION; 100 | } 101 | 102 | if (conf->readString(section, "listen_ip", App::LISTEN_IP, 103 | Size::IP_V4) || 104 | 0 == App::LISTEN_IP[0]) { 105 | strncpy(App::LISTEN_IP, DefaultConfig::App::LISTEN_IP, Size::IP_V4); 106 | } 107 | 108 | if (conf->readShort(section, "listen_port", &App::LISTEN_PORT) || 109 | 0 == App::LISTEN_PORT) { 110 | App::LISTEN_PORT = DefaultConfig::App::LISTEN_PORT; 111 | } 112 | 113 | if (conf->readInt(section, "epoll_wait_event_number", 114 | &App::EPOLL_WAIT_EVENT_NUM) || 115 | 0 == App::EPOLL_WAIT_EVENT_NUM) { 116 | App::EPOLL_WAIT_EVENT_NUM = 117 | DefaultConfig::App::EPOLL_WAIT_EVENT_NUM; 118 | } 119 | 120 | if (conf->readInt(section, "node_group_num", &App::NODE_GROUP_NUM) || 121 | 0 == App::NODE_GROUP_NUM) { 122 | App::NODE_GROUP_NUM = DefaultConfig::App::NODE_GROUP_NUM; 123 | } 124 | 125 | if (conf->readInt(section, "node_group_size", &App::NODE_GROUP_SIZE) || 126 | 0 == App::NODE_GROUP_SIZE) { 127 | App::NODE_GROUP_SIZE = DefaultConfig::App::NODE_GROUP_SIZE; 128 | } 129 | 130 | App::TOTAL_SUPPORT_USER_NUM = 131 | App::NODE_GROUP_NUM * App::NODE_GROUP_SIZE; 132 | App::TOTAL_THREAD_NUM = App::NODE_GROUP_NUM + 4; 133 | 134 | if (conf->readInt(section, "message_max_num_in_queue", 135 | &App::MESSAGE_MAX_NUM_IN_QUEUE) || 136 | 0 == App::MESSAGE_MAX_NUM_IN_QUEUE) { 137 | App::MESSAGE_MAX_NUM_IN_QUEUE = App::NODE_GROUP_SIZE * 8; 138 | } 139 | 140 | if (conf->readInt(section, "thread_stack_size", 141 | &App::THREAD_STACK_SIZE) || 142 | 0 == App::THREAD_STACK_SIZE) { 143 | App::THREAD_STACK_SIZE = DefaultConfig::App::THREAD_STACK_SIZE; 144 | } 145 | 146 | if (conf->readInt(section, "heartbeat_interval", 147 | &App::HEARTBEAT_INTERVAL) || 148 | 0 == App::HEARTBEAT_INTERVAL) { 149 | App::HEARTBEAT_INTERVAL = DefaultConfig::App::HEARTBEAT_INTERVAL; 150 | } 151 | } 152 | 153 | none_ initializeMsg(CConfReader *conf) { 154 | assert(null_v != conf); 155 | const c1_ *section = "msg"; 156 | 157 | ub4_ validDuration; 158 | 159 | if (conf->readInt(section, "valid_duration", &validDuration) || 160 | 0 == validDuration) { 161 | Msg::VALID_DURATION = 162 | DefaultConfig::Msg::VALID_DURATION * 60 * 60 * 1000000; 163 | } else { 164 | Msg::VALID_DURATION = validDuration * 60 * 60 * 1000000; 165 | } 166 | 167 | if (conf->readByte(section, "valid_number", &Msg::VALID_NUMBER) || 168 | 0 == Msg::VALID_NUMBER) { 169 | Msg::VALID_NUMBER = DefaultConfig::Msg::VALID_NUMBER; 170 | } 171 | } 172 | 173 | none_ initializeRedis(CConfReader *conf) { 174 | assert(null_v != conf); 175 | const c1_ *section = "redis"; 176 | 177 | if (conf->readString(section, "host", Redis::HOST, Size::URL) || 178 | 0 == Redis::HOST[0]) { 179 | strncpy(Redis::HOST, DefaultConfig::Redis::HOST, Size::URL); 180 | } 181 | 182 | if (conf->readShort(section, "port", &Redis::PORT) || 183 | 0 == Redis::PORT) { 184 | Redis::PORT = DefaultConfig::Redis::PORT; 185 | } 186 | 187 | if (conf->readInt(section, "timeout", &Redis::TIMEOUT) || 188 | 0 == Redis::TIMEOUT) { 189 | Redis::TIMEOUT = DefaultConfig::Redis::TIMEOUT; 190 | } 191 | 192 | if (conf->readString(section, "auth", Redis::AUTH, Size::PASSWORD) || 193 | 0 == Redis::AUTH[0]) { 194 | strncpy(Redis::AUTH, DefaultConfig::Redis::AUTH, Size::PASSWORD); 195 | } 196 | 197 | if (conf->readByte(section, "db", &Redis::DB)) { 198 | Redis::DB = DefaultConfig::Redis::DB; 199 | } 200 | } 201 | 202 | } // namespace Config 203 | -------------------------------------------------------------------------------- /src/config/Config.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : Config.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _CONFIG_H_ 12 | #define _CONFIG_H_ 13 | 14 | #include "../common/BaseType.h" 15 | #include "Size.h" 16 | 17 | namespace Config { 18 | 19 | #ifndef GLOBAL_CONFIG 20 | #define GLOBAL_CONFIG extern 21 | 22 | namespace App { 23 | 24 | GLOBAL_CONFIG c1_ CONF_FILE[Size::PATH]; 25 | GLOBAL_CONFIG ub4_ RUN_AS_DAEMON; 26 | GLOBAL_CONFIG c1_ NAME[Size::NAME]; 27 | GLOBAL_CONFIG ub4_ BASE_BUILD; 28 | GLOBAL_CONFIG ub2_ PROTOCOL_VERSION; 29 | GLOBAL_CONFIG c1_ LISTEN_IP[Size::IP_V4]; 30 | GLOBAL_CONFIG ub2_ LISTEN_PORT; 31 | GLOBAL_CONFIG ub4_ EPOLL_WAIT_EVENT_NUM; 32 | GLOBAL_CONFIG ub4_ NODE_GROUP_NUM; 33 | GLOBAL_CONFIG ub4_ NODE_GROUP_SIZE; 34 | GLOBAL_CONFIG ub4_ TOTAL_SUPPORT_USER_NUM; 35 | GLOBAL_CONFIG ub4_ TOTAL_THREAD_NUM; 36 | GLOBAL_CONFIG ub4_ MESSAGE_MAX_NUM_IN_QUEUE; 37 | GLOBAL_CONFIG ub4_ THREAD_STACK_SIZE; 38 | GLOBAL_CONFIG ub4_ HEARTBEAT_INTERVAL; 39 | 40 | } // namespace App 41 | 42 | namespace Msg { 43 | 44 | GLOBAL_CONFIG ub8_ VALID_DURATION; 45 | GLOBAL_CONFIG ub1_ VALID_NUMBER; 46 | 47 | } // namespace Msg 48 | 49 | namespace Redis { 50 | 51 | GLOBAL_CONFIG c1_ HOST[Size::URL]; 52 | GLOBAL_CONFIG ub2_ PORT; 53 | GLOBAL_CONFIG ub4_ TIMEOUT; // Seconds 54 | GLOBAL_CONFIG c1_ AUTH[Size::PASSWORD]; 55 | GLOBAL_CONFIG ub1_ DB; 56 | 57 | } // namespace Redis 58 | 59 | #endif 60 | 61 | none_ initialize(const c1_ *confFileName); 62 | 63 | } // namespace Config 64 | 65 | #endif // _CONFIG_H_ 66 | -------------------------------------------------------------------------------- /src/config/DefaultConfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : DefaultConfig.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _DEFAULT_CONFIG_H_ 12 | #define _DEFAULT_CONFIG_H_ 13 | 14 | #include "Size.h" 15 | 16 | namespace DefaultConfig { 17 | 18 | namespace App { 19 | 20 | const c1_ CONF_FILE[] = "gMaxLinked.conf"; 21 | 22 | const ub4_ RUN_AS_DAEMON = 0; 23 | 24 | const c1_ NAME[] = "gMaxLinked"; 25 | const ub4_ BASE_BUILD = 0; 26 | const ub2_ PROTOCOL_VERSION = 0x0100; 27 | 28 | const c1_ LISTEN_IP[] = "0.0.0.0"; 29 | const ub2_ LISTEN_PORT = 10505; 30 | 31 | const ub4_ EPOLL_WAIT_EVENT_NUM = 16; 32 | 33 | const ub4_ NODE_GROUP_NUM = 4; 34 | const ub4_ NODE_GROUP_SIZE = 16; 35 | 36 | const ub4_ THREAD_STACK_SIZE = 4096; // k 37 | 38 | const ub4_ HEARTBEAT_INTERVAL = 10; // s 39 | 40 | } // namespace App 41 | 42 | namespace Msg { 43 | 44 | const ub8_ VALID_DURATION = 24 * 60 * 60 * 1000000L; // us 45 | 46 | const ub1_ VALID_NUMBER = 8; 47 | } // namespace Msg 48 | 49 | namespace Redis { 50 | 51 | const c1_ HOST[] = "localhost"; 52 | const ub2_ PORT = 6379; 53 | const ub4_ TIMEOUT = 1; // s 54 | const c1_ AUTH[] = "123456"; 55 | const ub1_ DB = 0; 56 | } // namespace Redis 57 | 58 | } // namespace DefaultConfig 59 | 60 | #endif // _DEFAULT_CONFIG_H_ 61 | -------------------------------------------------------------------------------- /src/config/Length.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : Length.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _LENGTH_H_ 12 | #define _LENGTH_H_ 13 | 14 | namespace Length { 15 | 16 | const ub4_ NAME = 32; 17 | const ub4_ PASSWORD = 32; 18 | const ub4_ IP_V4 = 15; 19 | const ub4_ URL = 256; 20 | const ub4_ PATH = 256; 21 | 22 | // define your lengths here 23 | const ub4_ SESSION_ID = 16; 24 | const ub4_ JSON = 384; // 256 + 128 25 | 26 | } // namespace Length 27 | 28 | #endif // _LENGTH_H_ 29 | -------------------------------------------------------------------------------- /src/config/SConscript: -------------------------------------------------------------------------------- 1 | env = Environment() 2 | env.Append(CCFLAGS = '-std=c++11') 3 | 4 | objs = env.Object(Glob('*.cpp')) 5 | Return('objs') -------------------------------------------------------------------------------- /src/config/Size.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : Size.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _SIZE_H_ 12 | #define _SIZE_H_ 13 | 14 | #include "Length.h" 15 | 16 | namespace Size { 17 | 18 | const ub4_ NAME = Length::NAME + 1; 19 | const ub4_ PASSWORD = Length::PASSWORD + 1; 20 | const ub4_ IP_V4 = Length::IP_V4 + 1; 21 | const ub4_ URL = Length::URL + 1; 22 | const ub4_ PATH = Length::PATH + 1; 23 | 24 | // define your sizes here according to Length.h 25 | const ub4_ SESSION_ID = Length::SESSION_ID + 1; 26 | const ub4_ JSON = Length::JSON + 1; 27 | } // namespace Size 28 | 29 | #endif // _SIZE_H_ 30 | -------------------------------------------------------------------------------- /src/database/CRedisOperator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CRedisOperator.cpp 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #include "CRedisOperator.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "../config/Config.h" 19 | #include "../transaction/CTransaction.h" 20 | 21 | CRedisOperator::CRedisOperator() { _context = null_v; } 22 | 23 | CRedisOperator::~CRedisOperator() { 24 | if (_context) { 25 | redisFree(_context); 26 | } 27 | } 28 | 29 | bool_ CRedisOperator::connect() { 30 | if (_context) { 31 | redisFree(_context); 32 | } 33 | 34 | timeval timeout = {Config::Redis::TIMEOUT, 0}; 35 | 36 | _context = redisConnectWithTimeout(Config::Redis::HOST, Config::Redis::PORT, 37 | timeout); 38 | 39 | if (null_v == _context) { 40 | log_fatal("CRedisOperator:connect: cannot allocate redis context"); 41 | 42 | return false_v; 43 | } 44 | 45 | if (_context->err) { 46 | log_fatal( 47 | "CRedisOperator::connect: failed to call " 48 | "redisConnectWithTimeout-%s", 49 | _context->errstr); 50 | redisFree(_context); 51 | _context = null_v; 52 | 53 | return false_v; 54 | } 55 | 56 | redisReply *reply = 57 | (redisReply *)redisCommand(_context, "AUTH %s", Config::Redis::AUTH); 58 | 59 | if (!_errorHandler(reply, true_v)) { 60 | return false_v; 61 | } 62 | 63 | reply = 64 | (redisReply *)redisCommand(_context, "SELECT %u", Config::Redis::DB); 65 | 66 | if (!_errorHandler(reply, true_v)) { 67 | return false_v; 68 | } 69 | 70 | return true_v; 71 | } 72 | 73 | none_ CRedisOperator::disconnect() { 74 | if (_context) { 75 | redisFree(_context); 76 | _context = null_v; 77 | } 78 | } 79 | 80 | bool_ CRedisOperator::_errorHandler(obj_ reply, bool_ freeReply) { 81 | if (null_v == _context) { 82 | return false_v; 83 | } 84 | 85 | bool_ ret = true_v; 86 | redisReply *rr = (redisReply *)reply; 87 | 88 | if (!rr) { 89 | log_error("CRedisOperator::errorHandler: %s", _context->errstr); 90 | connect(); // reconnect 91 | ret = false_v; 92 | } else { 93 | if (REDIS_REPLY_ERROR == rr->type) { 94 | log_error("CRedisOperator::errorHandler: %s", rr->str); 95 | ret = false_v; 96 | freeReplyObject(rr); 97 | } else if (freeReply) { 98 | freeReplyObject(rr); 99 | } 100 | } 101 | 102 | return ret; 103 | } 104 | 105 | ub8_ CRedisOperator::verifyHandshake(const c1_ *sessionId) { 106 | assert(sessionId && 0 != sessionId[0]); 107 | if (!sessionId || 0 == sessionId[0]) { 108 | return 0; 109 | } 110 | 111 | assert(_context); 112 | if (null_v == _context) { 113 | log_fatal("CRedisOperator::verifyHandshake: no context"); 114 | 115 | return 0; 116 | } 117 | 118 | redisReply *reply = (redisReply *)redisCommand( 119 | _context, "zscore gml:account:session %s", sessionId); 120 | 121 | if (!_errorHandler(reply, false_v)) { 122 | return 0; 123 | } 124 | 125 | if (REDIS_REPLY_NIL == reply->type) { 126 | freeReplyObject(reply); 127 | 128 | return 0; 129 | } 130 | 131 | assert(REDIS_REPLY_STRING == reply->type); 132 | ub8_ id = (ub8_)atoll(reply->str); 133 | 134 | freeReplyObject(reply); 135 | 136 | return id; 137 | } 138 | 139 | bool_ CRedisOperator::sendMessage(const CTransaction *transaction, 140 | const Message::TPDUSendMsg *msg, 141 | ub8_ &messageId) { 142 | assert(transaction); 143 | assert(msg); 144 | assert(_context); 145 | if (null_v == _context) { 146 | log_fatal("CRedisOperator::sendMessage: no context"); 147 | 148 | return false_v; 149 | } 150 | 151 | redisReply *reply = (redisReply *)redisCommand( 152 | _context, "hincrby gml:message:key:generator messageId 1"); 153 | 154 | if (!_errorHandler(reply, false_v)) { 155 | return false_v; 156 | } 157 | 158 | assert(REDIS_REPLY_INTEGER == reply->type); 159 | messageId = (ub8_)reply->integer; 160 | 161 | freeReplyObject(reply); 162 | 163 | ub8_ ornExtId = 0; 164 | 165 | reply = (redisReply *)redisCommand( 166 | _context, 167 | "hmset gml:message:%lu ornType %u ornId %lu ornExtId %lu json %s", 168 | messageId, msg->dstType, transaction->getId(), msg->dstId, msg->json); 169 | 170 | if (!_errorHandler(reply, true_v)) { 171 | return false_v; 172 | } 173 | 174 | if (1 == msg->dstType) { 175 | reply = (redisReply *)redisCommand( 176 | _context, "zadd gml:message:queue:user:%lu %lu %lu", msg->dstId, 177 | CBase::now(), messageId); 178 | 179 | if (!_errorHandler(reply, true_v)) { 180 | return false_v; 181 | } 182 | } else if (2 == msg->dstType) { 183 | ub8_ now = CBase::now(); 184 | 185 | reply = (redisReply *)redisCommand( 186 | _context, "zadd gml:message:queue:group:%lu %lu %lu", msg->dstId, 187 | now, messageId); 188 | 189 | if (!_errorHandler(reply, true_v)) { 190 | return false_v; 191 | } 192 | 193 | reply = (redisReply *)redisCommand( 194 | _context, "zrangebyscore gml:group:members:%lu 1 1", msg->dstId); 195 | 196 | if (!_errorHandler(reply, false_v)) { 197 | return false_v; 198 | } 199 | 200 | // there is no members in the group, just ignore 201 | if (REDIS_REPLY_NIL == reply->type) { 202 | freeReplyObject(reply); 203 | 204 | return true_v; 205 | } 206 | 207 | assert(REDIS_REPLY_ARRAY == reply->type); 208 | b4_ i = 0; 209 | 210 | for (; i < reply->elements; i++) { 211 | assert(REDIS_REPLY_STRING == reply->element[i]->type); 212 | ub8_ userId = (ub8_)atoll(reply->element[i]->str); 213 | redisReply *subReply = (redisReply *)redisCommand( 214 | _context, "zadd gml:message:queue:user:%lu %lu %lu", userId, 215 | now, messageId); 216 | // do not check if it's successful 217 | 218 | freeReplyObject(subReply); 219 | } 220 | 221 | freeReplyObject(reply); 222 | } 223 | 224 | return true_v; 225 | } 226 | 227 | bool_ CRedisOperator::checkMessages(CTransaction *transaction) { 228 | assert(transaction); 229 | assert(_context); 230 | if (null_v == _context) { 231 | log_fatal("CRedisOperator::checkMessages: no context"); 232 | 233 | return false_v; 234 | } 235 | 236 | ub8_ now = CBase::now(); 237 | assert(now > transaction->getLastUpdate()); 238 | redisReply *reply; 239 | 240 | if (0 == transaction->getLastUpdate() || 241 | Config::Msg::VALID_DURATION < now - transaction->getLastUpdate()) { 242 | redisReply *subReply = (redisReply *)redisCommand( 243 | _context, "zcount gml:message:queue:user:%lu -inf +inf", 244 | transaction->getId()); 245 | 246 | if (!_errorHandler(subReply, false_v)) { 247 | return false_v; 248 | } 249 | 250 | assert(REDIS_REPLY_INTEGER == subReply->type); 251 | int num = subReply->integer; 252 | freeReplyObject(subReply); 253 | 254 | if (0 >= num) { 255 | return false_v; 256 | } else if (Config::Msg::VALID_NUMBER <= num) { 257 | num -= Config::Msg::VALID_NUMBER; 258 | } else { 259 | num = 0; 260 | } 261 | 262 | reply = (redisReply *)redisCommand( 263 | _context, 264 | "zrangebyscore gml:message:queue:user:%lu -inf +inf withscores " 265 | "limit %d %u", 266 | transaction->getId(), num, Config::Msg::VALID_NUMBER); 267 | } else { 268 | reply = (redisReply *)redisCommand( 269 | _context, 270 | "zrangebyscore gml:message:queue:user:%lu (%lu +inf withscores " 271 | "limit 0 %u", 272 | transaction->getId(), transaction->getLastUpdate(), 273 | Config::Msg::VALID_DURATION); 274 | } 275 | 276 | if (!_errorHandler(reply, false_v)) { 277 | return false_v; 278 | } 279 | 280 | if (REDIS_REPLY_NIL == reply->type) { 281 | freeReplyObject(reply); 282 | 283 | return false_v; 284 | } 285 | 286 | assert(REDIS_REPLY_ARRAY == reply->type); 287 | b4_ n = reply->elements / 2; 288 | 289 | for (b4_ i = 0; i < n; i++) { 290 | assert(REDIS_REPLY_STRING == reply->element[i * 2]->type); 291 | assert(REDIS_REPLY_STRING == reply->element[i * 2 + 1]->type); 292 | redisReply *subReply = (redisReply *)redisCommand( 293 | _context, "hmget gml:message:%s ornType ornId ornExtId json", 294 | reply->element[i * 2]->str); 295 | 296 | if (!_errorHandler(subReply, false_v)) { 297 | freeReplyObject(reply); 298 | 299 | return false_v; 300 | } 301 | 302 | assert(REDIS_REPLY_ARRAY == subReply->type); 303 | assert(4 == subReply->elements); 304 | if (REDIS_REPLY_NIL == subReply->element[0]->type || 305 | REDIS_REPLY_NIL == subReply->element[1]->type || 306 | REDIS_REPLY_NIL == subReply->element[2]->type || 307 | REDIS_REPLY_NIL == subReply->element[3]->type) { 308 | freeReplyObject(subReply); 309 | 310 | continue; 311 | } 312 | 313 | ub1_ ornType = (ub1_)atoi(subReply->element[0]->str); 314 | ub8_ ornId = (ub8_)atoll(subReply->element[1]->str); 315 | ub8_ ornExtId = (ub8_)atoll(subReply->element[2]->str); 316 | bool_ result = transaction->handlePushMessage( 317 | ornType, ornId, ornExtId, (ub8_)atoll(reply->element[i * 2]->str), 318 | subReply->element[3]->str, subReply->element[3]->len, 319 | (ub8_)atoll(reply->element[i * 2 + 1]->str)); 320 | 321 | if (false_v == result) { 322 | freeReplyObject(subReply); 323 | freeReplyObject(reply); 324 | 325 | return false_v; 326 | } 327 | 328 | freeReplyObject(subReply); 329 | } 330 | 331 | freeReplyObject(reply); 332 | 333 | return true_v; 334 | } 335 | -------------------------------------------------------------------------------- /src/database/CRedisOperator.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CRedisOperator.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_REDIS_OPERATOR_H_ 12 | #define _C_REDIS_OPERATOR_H_ 13 | 14 | #include 15 | 16 | #include "../common/CBase.h" 17 | #include "../traffic/Message.h" 18 | 19 | class CTransaction; 20 | 21 | class CRedisOperator : public CBase { 22 | public: 23 | CRedisOperator(); 24 | virtual ~CRedisOperator(); 25 | 26 | ub8_ verifyHandshake(const c1_ *sessionId); 27 | bool_ sendMessage(const CTransaction *transaction, 28 | const Message::TPDUSendMsg *msg, ub8_ &messageId); 29 | bool_ checkMessages(CTransaction *transaction); 30 | 31 | bool_ connect(); 32 | none_ disconnect(); 33 | 34 | private: 35 | redisContext *_context; 36 | 37 | bool_ _errorHandler(obj_ reply, bool_ freeReply = true_v); 38 | }; 39 | 40 | #endif // _C_REDIS_OPERATOR_H_ 41 | -------------------------------------------------------------------------------- /src/database/RedisStructure.md: -------------------------------------------------------------------------------- 1 | redis structure for this application 2 | =========================== 3 | Please refer to accont.redis.md and message.redis.md and group.redis.md in RESTfulServer 4 | -------------------------------------------------------------------------------- /src/database/SConscript: -------------------------------------------------------------------------------- 1 | env = Environment() 2 | env.Append(CCFLAGS = '-std=c++11') 3 | 4 | objs = env.Object(Glob('*.cpp')) 5 | Return('objs') -------------------------------------------------------------------------------- /src/gMaxLinked.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : gMaxLinked.cpp 4 | Author : Rafael Gu 5 | Version : 1.0.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 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 | 25 | #include "./config/Config.h" 26 | #include "./transaction/CTransactionManager.h" 27 | 28 | const c1_ DAEMON_NAME[] = "gMaxLinked"; 29 | const c1_ DAEMON_VER[] = "1.0.0"; 30 | const c1_ RUN_AS_USER[] = "root"; 31 | const c1_ LOCK_FILE[] = "/var/lock/subsys/gMaxLinked"; 32 | const c1_ PID_FILE[] = "/var/run/gMaxLinked.pid"; 33 | 34 | static none_ sigHandler(b4_ sigNo) { 35 | switch (sigNo) { 36 | case SIGALRM: 37 | exit(EXIT_FAILURE); 38 | break; 39 | case SIGUSR1: 40 | exit(EXIT_SUCCESS); 41 | break; 42 | case SIGCHLD: 43 | exit(EXIT_FAILURE); 44 | break; 45 | } 46 | } 47 | 48 | static none_ runAsDaemon() { 49 | pid_t pid, sid, parent; 50 | 51 | // already a daemon 52 | if (getppid() == 1) { 53 | return; 54 | } 55 | 56 | // create the lock file as the current user 57 | if (open(LOCK_FILE, O_RDWR | O_CREAT, 0640) < 0) { 58 | syslog(LOG_ERR, "unable to create lock file %s, code=%d (%s)", 59 | LOCK_FILE, errno, strerror(errno)); 60 | exit(EXIT_FAILURE); 61 | } 62 | 63 | // drop user if there is one, and we were run as root 64 | if (getuid() == 0 || geteuid() == 0) { 65 | struct passwd *pw = getpwnam(RUN_AS_USER); 66 | 67 | if (pw) { 68 | syslog(LOG_NOTICE, "setting user to %s", RUN_AS_USER); 69 | setuid(pw->pw_uid); 70 | } 71 | } 72 | 73 | // trap signals that we expect to receive 74 | signal(SIGCHLD, sigHandler); 75 | signal(SIGUSR1, sigHandler); 76 | signal(SIGALRM, sigHandler); 77 | 78 | // fork off the parent process 79 | pid = fork(); 80 | 81 | if (pid < 0) { 82 | syslog(LOG_ERR, "unable to fork daemon, code=%d (%s)", errno, 83 | strerror(errno)); 84 | exit(EXIT_FAILURE); 85 | } 86 | 87 | // if we got a good PID, then we can exit the parent process 88 | if (pid > 0) { 89 | // wait for confirmation from the child via SIGTERM or SIGCHLD, or for 90 | // two seconds to elapse (SIGALRM). 91 | // pause() should not return 92 | alarm(2); 93 | pause(); 94 | 95 | exit(EXIT_FAILURE); 96 | } 97 | 98 | // at this pob4_ we are executing as the child process 99 | parent = getppid(); 100 | 101 | // cancel certain signals 102 | signal(SIGCHLD, SIG_DFL); // a child process dies 103 | signal(SIGTSTP, SIG_IGN); // various TTY signals 104 | signal(SIGTTOU, SIG_IGN); 105 | signal(SIGTTIN, SIG_IGN); 106 | signal(SIGHUP, SIG_IGN); // ignore hangup signal 107 | signal(SIGTERM, SIG_DFL); // die on SIGTERM 108 | 109 | // change the file mode mask 110 | umask(0); 111 | 112 | // create a new SID for the child process 113 | sid = setsid(); 114 | 115 | if (sid < 0) { 116 | syslog(LOG_ERR, "unable to create a new session, code %d (%s)", errno, 117 | strerror(errno)); 118 | exit(EXIT_FAILURE); 119 | } 120 | 121 | // change the current working directory 122 | // this prevents the current directory from being locked; hence not being 123 | // able to remove it 124 | if ((chdir("/tmp")) < 0) { 125 | syslog(LOG_ERR, "unable to change directory to %s, code %d (%s)", 126 | "/tmp", errno, strerror(errno)); 127 | exit(EXIT_FAILURE); 128 | } 129 | 130 | // redirect standard files to /dev/null 131 | freopen("/dev/null", "r", stdin); 132 | freopen("/dev/null", "w", stdout); 133 | freopen("/dev/null", "w", stderr); 134 | 135 | // tell the parent process that we are A-okay 136 | kill(parent, SIGUSR1); 137 | 138 | // create the pid file 139 | b4_ fd = open(PID_FILE, O_RDWR | O_CREAT, 0640); 140 | 141 | if (fd < 0) { 142 | syslog(LOG_ERR, "unable to create pid file %s, code=%d (%s)", PID_FILE, 143 | errno, strerror(errno)); 144 | exit(EXIT_FAILURE); 145 | } 146 | 147 | // get pid and write b4_o PID_FILE 148 | b1_ temp[16]; 149 | 150 | pid = getpid(); 151 | memset(temp, 0, 16); 152 | sprintf(temp, "%d", pid); 153 | write(fd, temp, strlen(temp) + 1); 154 | close(fd); 155 | } 156 | 157 | // we need to configure the system manually to make the feature work 158 | // shell(as root): ulimit -c unlimited 159 | static none_ enableDump() { 160 | // core dump 161 | struct rlimit res; 162 | 163 | res.rlim_cur = RLIM_INFINITY; 164 | res.rlim_max = RLIM_INFINITY; 165 | 166 | setrlimit(RLIMIT_CORE, &res); 167 | } 168 | 169 | static b4_ checkParameters(b4_ argc, c1_ **argv) { 170 | if (1 == argc) { 171 | return 0; 172 | } else if (2 == argc) { 173 | if (0 == strcmp("-version", argv[1])) { 174 | printf("Version: %s\n", DAEMON_VER); 175 | 176 | return 1; 177 | } else { 178 | return 2; 179 | } 180 | } else { 181 | return -1; 182 | } 183 | } 184 | 185 | static none_ run(b4_ argc, c1_ **argv) { 186 | // load configure file 187 | c1_ *conf = null_v; 188 | 189 | if (2 == argc) { 190 | conf = argv[1]; 191 | } 192 | 193 | Config::initialize(conf); 194 | log_info("%s will start.", DAEMON_NAME); 195 | 196 | if (Config::App::RUN_AS_DAEMON) { 197 | runAsDaemon(); 198 | } 199 | 200 | log_info("%s is working ...", DAEMON_NAME); 201 | CTransactionManager::instance()->work(); 202 | 203 | log_fatal("%s will end.", DAEMON_NAME); 204 | CWorker::stop(); 205 | 206 | while (CWorker::getTotalNum()) { 207 | CBase::sleep(1, 0); // sleep for 1 second 208 | } 209 | 210 | log_info("%s is ended.", DAEMON_NAME); 211 | } 212 | 213 | b4_ main(b4_ argc, c1_ **argv) { 214 | signal(SIGPIPE, SIG_IGN); 215 | 216 | if (!CBase::initialize()) { 217 | return 1; 218 | } 219 | 220 | // initialize the logging b4_erface 221 | openlog(DAEMON_NAME, LOG_USER | LOG_PID, LOG_LOCAL5); 222 | syslog(LOG_INFO, "starting..."); 223 | 224 | // enable dump to catch the bugs and issues 225 | enableDump(); 226 | 227 | int ret = checkParameters(argc, argv); 228 | 229 | // run loop 230 | if (0 == ret || 2 == ret) { 231 | run(argc, argv); 232 | } else if (1 == ret) { 233 | // version print 234 | } else { 235 | syslog(LOG_NOTICE, 236 | "Please use: %s -version or %s [configure file path]", 237 | DAEMON_NAME, DAEMON_NAME); 238 | } 239 | 240 | // finish up 241 | syslog(LOG_NOTICE, "terminated"); 242 | closelog(); 243 | 244 | CBase::uninitialize(); 245 | 246 | return 0; 247 | } 248 | -------------------------------------------------------------------------------- /src/traffic/CNode.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CNode.cpp 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #include "CNode.h" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "../config/Config.h" 18 | #include "CNodeGroup.h" 19 | 20 | CNode::CNode() : _transaction(this) { 21 | memset(_ip, 0, Size::IP_V4); 22 | _port = 0; 23 | _fd = 0; 24 | _group = null_v; 25 | _recvOffset = 0; 26 | _recvDueSize = 0; 27 | } 28 | 29 | CNode::~CNode() {} 30 | 31 | bool_ CNode::onAttach(CNodeGroup *group, const c1_ *ip, ub2_ port, b4_ fd) { 32 | _group = group; 33 | _recvOffset = 0; 34 | _recvDueSize = 0; 35 | strncpy(_ip, ip, Length::IP_V4); 36 | _port = port; 37 | _fd = fd; 38 | 39 | return _transaction.onAttach(); 40 | } 41 | 42 | none_ CNode::onDetach() { 43 | _transaction.onDetach(); 44 | _fd = 0; 45 | _port = 0; 46 | memset(_ip, 0, Size::IP_V4); 47 | _recvOffset = 0; 48 | _recvDueSize = 0; 49 | _group = null_v; 50 | } 51 | 52 | bool_ CNode::recv() { 53 | ssize_t n; 54 | 55 | for (;;) { 56 | if (0 == _recvDueSize) { // parser header 57 | n = read(_fd, _recvBuffer + _recvOffset, 58 | sizeof(Message::THeader) - _recvOffset); 59 | 60 | if (0 == n) { 61 | // the socket has been closed 62 | return false_v; 63 | } 64 | 65 | if (0 > n) { 66 | if (EAGAIN == errno) { 67 | break; 68 | } 69 | 70 | _handleRecvErrors(); 71 | 72 | return false_v; 73 | } 74 | 75 | log_debug("[%p %s:%u]CNode::recv: got %lu bytes data", 76 | &_transaction, _ip, _port, n); 77 | _recvOffset += n; 78 | 79 | if (sizeof(Message::THeader) == _recvOffset) { 80 | _recvDueSize = *(ub2_ *)_recvBuffer; 81 | 82 | // maybe the whole PDU is just a header 83 | if (sizeof(Message::THeader) == _recvDueSize) { 84 | Message::TMsg *msg = (Message::TMsg *)_recvBuffer; 85 | 86 | msg->ext = (ub8_)&_transaction; 87 | 88 | if (false_v == _group->putMessage(msg)) { 89 | log_crit("[%p %s:%u]CNode::recv: no more queue space", 90 | &_transaction, _ip, _port); 91 | 92 | return false_v; 93 | } 94 | 95 | _recvOffset = 0; 96 | _recvDueSize = 0; 97 | } 98 | } 99 | } else { // parser body and others 100 | n = read(_fd, _recvBuffer + _recvOffset, 101 | _recvDueSize - _recvOffset); 102 | 103 | if (0 == n) { 104 | // the socket has been closed 105 | return false_v; 106 | } 107 | 108 | if (0 > n) { 109 | if (EAGAIN == errno) { 110 | break; 111 | } 112 | 113 | _handleRecvErrors(); 114 | 115 | return false_v; 116 | } 117 | 118 | log_debug("[%p %s:%u]CNode::recv: got %lu bytes data", 119 | &_transaction, _ip, _port, n); 120 | _recvOffset += n; 121 | 122 | if (_recvDueSize == _recvOffset) { 123 | Message::TMsg *msg = (Message::TMsg *)_recvBuffer; 124 | 125 | msg->ext = (ub8_)&_transaction; 126 | 127 | if (false_v == _group->putMessage(msg)) { 128 | log_crit("[%p %s:%u]CNode::recv: no more queue space", 129 | &_transaction, _ip, _port); 130 | 131 | return false_v; 132 | } 133 | 134 | _recvOffset = 0; 135 | _recvDueSize = 0; 136 | } 137 | } 138 | } 139 | 140 | return true_v; 141 | } 142 | 143 | bool_ CNode::send(Message::TMsg *msg) { 144 | msg->ext = 0; 145 | 146 | ssize_t n; 147 | ub1_ *buffer = (ub1_ *)msg; 148 | ub4_ offset = 0; 149 | 150 | do { 151 | n = write(_fd, buffer + offset, msg->size - offset); 152 | 153 | if (0 < n) { 154 | log_debug("[%p %s:%u]CNode::send: send %lu bytes data", 155 | &_transaction, _ip, _port, n); 156 | offset += n; 157 | } else if (0 == n) { 158 | // the socket has been closed 159 | return false_v; 160 | } else { 161 | if (EAGAIN == errno) { 162 | sleep(0, 10); 163 | 164 | continue; 165 | } 166 | 167 | c1_ error[8] = {0}; 168 | 169 | switch (errno) { 170 | case EBADF: 171 | strncpy(error, "EBADF", sizeof("EBADF")); 172 | break; 173 | case EFAULT: 174 | strncpy(error, "EFAULT", sizeof("EFAULT")); 175 | break; 176 | case EFBIG: 177 | strncpy(error, "EFBIG", sizeof("EFBIG")); 178 | break; 179 | case EINTR: 180 | strncpy(error, "IINTR", sizeof("IINTR")); 181 | break; 182 | case EINVAL: 183 | strncpy(error, "EINVAL", sizeof("EINVAL")); 184 | break; 185 | case EIO: 186 | strncpy(error, "EIO", sizeof("EIO")); 187 | break; 188 | case ENOSPC: 189 | strncpy(error, "ENOSPC", sizeof("ENOSPC")); 190 | break; 191 | case EPIPE: 192 | strncpy(error, "EPIPE", sizeof("EPIPE")); 193 | break; 194 | default: 195 | strncpy(error, "Unknown", sizeof("Unknown")); 196 | assert(false_v); 197 | break; 198 | } 199 | 200 | log_error( 201 | "[%p %s:%d]CNode::send: error-%s" 202 | " when sending data.", 203 | &_transaction, _ip, _port, error); 204 | 205 | return false_v; 206 | } 207 | } while (msg->size > offset); 208 | 209 | return true_v; 210 | } 211 | 212 | none_ CNode::_handleRecvErrors() { 213 | c1_ error[8] = {0}; 214 | 215 | switch (errno) { 216 | case EBADF: 217 | strncpy(error, "EBADF", sizeof("EBADF")); 218 | break; 219 | case EFAULT: 220 | strncpy(error, "EFAULT", sizeof("EFAULT")); 221 | break; 222 | case EINTR: 223 | strncpy(error, "IINTR", sizeof("IINTR")); 224 | break; 225 | case EINVAL: 226 | strncpy(error, "EINVAL", sizeof("EINVAL")); 227 | break; 228 | case EIO: 229 | strncpy(error, "EIO", sizeof("EIO")); 230 | break; 231 | case EISDIR: 232 | strncpy(error, "EISDIR", sizeof("EISDIR")); 233 | break; 234 | default: 235 | strncpy(error, "Unknown", sizeof("Unknown")); 236 | assert(false_v); 237 | break; 238 | } 239 | 240 | log_error("[%p %s:%u]CNode::recv: error-%s", &_transaction, _ip, _port, 241 | error); 242 | } 243 | -------------------------------------------------------------------------------- /src/traffic/CNode.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CNode.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_NODE_H_ 12 | #define _C_NODE_H_ 13 | 14 | #include "../transaction/CTransaction.h" 15 | #include "CNodeGroup.h" 16 | #include "Message.h" 17 | 18 | class CNode : public CBase { 19 | public: 20 | CNode(); 21 | virtual ~CNode(); 22 | 23 | // called by CTrafficManager thread when CNodeGroup attaches the node 24 | bool_ onAttach(CNodeGroup *node, const c1_ *ip, ub2_ port, b4_ fd); 25 | 26 | // called by CTrafficManager thread when CNodeGroup detaches the node 27 | none_ onDetach(); 28 | 29 | // receive data from client and put them into the input queue 30 | // called by CTrafficManager thread 31 | bool_ recv(); 32 | 33 | // send data to client 34 | // called by CNodeGroup thread 35 | bool_ send(Message::TMsg *msg); 36 | 37 | const c1_ *getIp() const { return _ip; } 38 | 39 | ub2_ getPort() const { return _port; } 40 | 41 | b4_ getFd() const { return _fd; } 42 | 43 | CTransaction *getTransaction() { return &_transaction; } 44 | 45 | CNodeGroup *getGroup() { return _group; } 46 | 47 | private: 48 | c1_ _ip[Size::IP_V4]; 49 | ub2_ _port; 50 | 51 | // socket file descriptor 52 | b4_ _fd; 53 | 54 | // corresponding unite on upper layer 55 | CTransaction _transaction; 56 | 57 | // thread to run the node and corresponding transaction 58 | CNodeGroup *_group; 59 | 60 | // message buffer 61 | ub2_ _recvOffset; 62 | ub2_ _recvDueSize; 63 | ub1_ _recvBuffer[Message::MSG_MAX_LENGTH]; 64 | 65 | none_ _handleRecvErrors(); 66 | }; 67 | 68 | #endif // _C_NODE_H_ 69 | -------------------------------------------------------------------------------- /src/traffic/CNodeGroup.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CNodeGroup.cpp 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #include "CNodeGroup.h" 12 | 13 | #include "../config/Config.h" 14 | #include "../transaction/CTransaction.h" 15 | #include "CNode.h" 16 | #include "CTrafficManager.h" 17 | 18 | CNodeGroup::CNodeGroup() 19 | : _worker(Config::App::THREAD_STACK_SIZE), 20 | _queue(Config::App::MESSAGE_MAX_NUM_IN_QUEUE * Message::MSG_MAX_LENGTH, 21 | &_mutex) { 22 | _pos = _container.end(); 23 | _worker.work(this, true_v); 24 | } 25 | 26 | CNodeGroup::~CNodeGroup() {} 27 | 28 | bool_ CNodeGroup::attach(CNode *node, const c1_ *ip, ub2_ port, b4_ fd) { 29 | assert(node); 30 | CAutoLock al(&_mutex); 31 | 32 | if (false_v == node->onAttach(this, ip, port, fd)) { 33 | return false_v; 34 | } 35 | 36 | if (0 == _container.size()) { 37 | _ro.connect(); 38 | } 39 | 40 | _container.push_back(node); 41 | 42 | return true_v; 43 | } 44 | 45 | none_ CNodeGroup::detach(CNode *node) { 46 | assert(node); 47 | CAutoLock al(&_mutex); 48 | 49 | if (*_pos == node) { 50 | _pos++; 51 | } 52 | 53 | _container.remove(node); 54 | 55 | if (0 == _container.size()) { 56 | _ro.disconnect(); 57 | } 58 | 59 | node->onDetach(); 60 | } 61 | 62 | bool_ CNodeGroup::putMessage(const Message::TMsg *msg) { 63 | assert(msg); 64 | log_debug("[%p %p]CNodeGroup::putMessage: write a message", (obj_)msg->ext, 65 | this); 66 | 67 | if (!_queue.write((const ub1_ *)msg, msg->size)) { 68 | log_crit( 69 | "[%p %p]CNodeGroup::putMessage: the size of queue is too " 70 | "small", 71 | (obj_)msg->ext, this); 72 | 73 | return false_v; 74 | } 75 | 76 | return true_v; 77 | } 78 | 79 | bool_ CNodeGroup::working() { 80 | bool_ rollingQueue = _rollingQueue(); 81 | bool_ rollingNode = _rollingNode(); 82 | 83 | if (false_v == rollingQueue && false_v == rollingNode) { 84 | sleep(0, 50); 85 | } 86 | 87 | return true_v; 88 | } 89 | 90 | bool_ CNodeGroup::_rollingQueue() { 91 | if (0 == _queue.getUsedSize()) { 92 | return false_v; 93 | } 94 | 95 | ub4_ n = _queue.read(_buffer, sizeof(ub2_)); 96 | assert(sizeof(ub2_) == n); 97 | 98 | ub2_ size = *(ub2_ *)_buffer; 99 | 100 | n = _queue.read(_buffer + sizeof(ub2_), size - sizeof(ub2_)); 101 | 102 | if (n + sizeof(ub2_) != size) { 103 | log_error("[%p]CNodeGroup::rollingQueue: size doesn't match", this); 104 | 105 | return false_v; 106 | } 107 | 108 | Message::TMsg *msg = (Message::TMsg *)_buffer; 109 | assert(msg->ext); 110 | 111 | log_debug("[%p %p]CNodeGroup::rollingQueue: read a message", (obj_)msg->ext, 112 | this); 113 | 114 | CTransaction *transaction = (CTransaction *)msg->ext; 115 | ETransactionStatus status = transaction->getStatus(); 116 | 117 | if (ETransactionStatus::CONNECTED == status || 118 | ETransactionStatus::READY == status) { 119 | if (!transaction->onMessage(msg)) { 120 | assert(ETransactionStatus::OVER == transaction->getStatus()); 121 | CTrafficManager::instance()->recycleNode(transaction->getNode()); 122 | } 123 | } 124 | 125 | return true_v; 126 | } 127 | 128 | bool_ CNodeGroup::_rollingNode() { 129 | _mutex.lock(); 130 | 131 | if (0 == _container.size()) { 132 | _mutex.unlock(); 133 | 134 | return false_v; 135 | } 136 | 137 | if (_container.end() == _pos) { 138 | _pos = _container.begin(); 139 | } 140 | 141 | CNode *node = *_pos++; 142 | 143 | _mutex.unlock(); 144 | 145 | CTransaction *transaction = node->getTransaction(); 146 | ETransactionStatus status = transaction->getStatus(); 147 | 148 | if (ETransactionStatus::READY == status) { 149 | transaction->onCheck(); 150 | } 151 | 152 | return true_v; 153 | } 154 | -------------------------------------------------------------------------------- /src/traffic/CNodeGroup.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CNodeGroup.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_NODE_GROUP_H_ 12 | #define _C_NODE_GROUP_H_ 13 | 14 | #include "../common/CBase.h" 15 | #include "../common/CLoopBuffer.h" 16 | #include "../common/CMutex.h" 17 | #include "../common/CWorker.h" 18 | #include "../common/IWorkable.h" 19 | #include "../database/CRedisOperator.h" 20 | #include "Message.h" 21 | 22 | class CNode; 23 | 24 | #include 25 | 26 | typedef std::list Container; 27 | 28 | class CNodeGroup : public CBase, public IWorkable { 29 | public: 30 | CNodeGroup(); 31 | virtual ~CNodeGroup(); 32 | 33 | // called by CTrafficManager thread 34 | bool_ attach(CNode *node, const c1_ *ip, ub2_ port, b4_ fd); 35 | 36 | // called by CTrafficManager thread 37 | none_ detach(CNode *node); 38 | 39 | // called by CTrafficManager thread or CTransactionManager thread 40 | bool_ putMessage(const Message::TMsg *msg); 41 | 42 | CRedisOperator &ro() { return _ro; } 43 | 44 | virtual bool_ working(); 45 | 46 | private: 47 | CWorker _worker; 48 | CMutex _mutex; 49 | 50 | CLoopBuffer _queue; 51 | ub1_ _buffer[Message::MSG_MAX_LENGTH]; 52 | 53 | CRedisOperator _ro; 54 | 55 | Container _container; 56 | Container::iterator _pos; 57 | 58 | bool_ _rollingQueue(); 59 | bool_ _rollingNode(); 60 | }; 61 | 62 | #endif // _C_NODE_GROUP_H_ 63 | -------------------------------------------------------------------------------- /src/traffic/CTrafficManager.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CTrafficManager.cpp 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #include "CTrafficManager.h" 12 | 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 | 27 | #include "../config/Config.h" 28 | #include "CNode.h" 29 | #include "CNodeGroup.h" 30 | 31 | CTrafficManager *CTrafficManager::_tm = null_v; 32 | 33 | CTrafficManager *CTrafficManager::instance() { 34 | if (!_tm) { 35 | _tm = new CTrafficManager(); 36 | } 37 | 38 | return _tm; 39 | } 40 | 41 | none_ CTrafficManager::destory() { 42 | delete _tm; 43 | _tm = null_v; 44 | } 45 | 46 | CTrafficManager::CTrafficManager() 47 | : _res(Config::App::TOTAL_SUPPORT_USER_NUM), 48 | _worker(Config::App::THREAD_STACK_SIZE) { 49 | _groups = new CNodeGroup[Config::App::NODE_GROUP_NUM]; 50 | 51 | _listenFd = 0; 52 | _epollFd = 0; 53 | 54 | _curGroupIndex = 0; 55 | 56 | _running = true_v; 57 | } 58 | 59 | CTrafficManager::~CTrafficManager() { 60 | _running = false_v; 61 | 62 | if (_listenFd) { 63 | close(_listenFd); 64 | } 65 | 66 | if (_epollFd) { 67 | close(_epollFd); 68 | } 69 | 70 | delete[] _groups; 71 | } 72 | 73 | none_ CTrafficManager::work() { 74 | // create socket to listen. 75 | // SOCK_NONBLOCK is not supported by less than glibc 2.9 76 | _listenFd = socket(AF_INET, SOCK_STREAM, 0); 77 | 78 | if (-1 == _listenFd) { 79 | log_fatal("CTrafficManager::work: failed to create listening socket"); 80 | 81 | exit(0); 82 | } 83 | 84 | _setNonBlocking(_listenFd); 85 | 86 | b4_ optVal = 1; 87 | 88 | if (setsockopt(_listenFd, SOL_SOCKET, SO_REUSEADDR, &optVal, sizeof(b4_))) { 89 | log_fatal( 90 | "CTrafficManager::work: failed to setsockopt with " 91 | "SO_REUSEADDR"); 92 | 93 | exit(0); 94 | } 95 | 96 | struct sockaddr_in serverAddr; 97 | 98 | memset(&serverAddr, 0, sizeof(struct sockaddr_in)); 99 | serverAddr.sin_family = AF_INET; 100 | inet_aton(Config::App::LISTEN_IP, &serverAddr.sin_addr); 101 | serverAddr.sin_port = htons(Config::App::LISTEN_PORT); 102 | 103 | if (-1 == 104 | bind(_listenFd, (struct sockaddr *)&serverAddr, sizeof(serverAddr))) { 105 | log_fatal("CTrafficManager::work: failed to bind listen socket"); 106 | 107 | exit(0); 108 | } 109 | 110 | _epollFd = epoll_create(Config::App::TOTAL_SUPPORT_USER_NUM + 1); 111 | 112 | if (-1 == _epollFd) { 113 | log_fatal("CTrafficManager::work: failed to call epoll_create"); 114 | 115 | exit(0); 116 | } 117 | 118 | // register listening fd into epoll. 119 | struct epoll_event ev; 120 | 121 | ev.events = EPOLLIN | EPOLLET; 122 | ev.data.fd = _listenFd; 123 | 124 | if (-1 == epoll_ctl(_epollFd, EPOLL_CTL_ADD, _listenFd, &ev)) { 125 | switch (errno) { 126 | case EBADF: 127 | log_fatal( 128 | "CTrafficManager::work: failed to call " 129 | "epoll_ctrl-EBADF"); 130 | break; 131 | case EEXIST: 132 | log_fatal( 133 | "CTrafficManager::work: failed to call " 134 | "epoll_ctrl-EEXIST"); 135 | break; 136 | case EINVAL: 137 | log_fatal( 138 | "CTrafficManager::work: failed to call " 139 | "epoll_ctrl-EINVAL"); 140 | break; 141 | case ENOENT: 142 | log_fatal( 143 | "CTrafficManager::work: failed to call " 144 | "epoll_ctrl-ENOENT"); 145 | break; 146 | case ENOMEM: 147 | log_fatal( 148 | "CTrafficManager::work: failed to call " 149 | "epoll_ctrl-ENOMEM"); 150 | break; 151 | case ENOSPC: 152 | log_fatal( 153 | "CTrafficManager::work: failed to call " 154 | "epoll_ctrl-ENOSPC"); 155 | break; 156 | case EPERM: 157 | log_fatal( 158 | "CTrafficManager::work: failed to call " 159 | "epoll_ctrl-EPERM"); 160 | break; 161 | default: 162 | log_fatal( 163 | "CTrafficManager::work: failed to call " 164 | "epoll_ctrl-Unknown"); 165 | } 166 | 167 | exit(0); 168 | } 169 | 170 | // start to listen. 171 | if (-1 == listen(_listenFd, Config::App::EPOLL_WAIT_EVENT_NUM)) { 172 | log_fatal("CTrafficManager::work: failed to start listening"); 173 | 174 | return; 175 | } 176 | 177 | _worker.work(this, true_v, true_v); 178 | } 179 | 180 | bool_ CTrafficManager::working() { 181 | // recycle nodes at first 182 | _mutex.lock(); 183 | 184 | while (!_nodeQueue.empty()) { 185 | _delNode(_nodeQueue.front()); 186 | _nodeQueue.pop(); 187 | } 188 | 189 | _mutex.unlock(); 190 | 191 | struct epoll_event events[Config::App::EPOLL_WAIT_EVENT_NUM]; 192 | 193 | // waiting until timeout (1 second) 194 | b4_ fds = 195 | epoll_wait(_epollFd, events, Config::App::EPOLL_WAIT_EVENT_NUM, 1000); 196 | 197 | if (-1 == fds) { 198 | switch (errno) { 199 | case EBADF: 200 | log_fatal( 201 | "CTrafficManager::working: failed to call " 202 | "epoll_wait-EBADF"); 203 | return false_v; 204 | case EFAULT: 205 | log_fatal( 206 | "CTrafficManager::working: failed to call " 207 | "epoll_wait-EFAULT"); 208 | return false_v; 209 | case EINTR: 210 | return true_v; 211 | case EINVAL: 212 | log_fatal( 213 | "CTrafficManager::working: failed to call " 214 | "epoll_wait-EINVAL."); 215 | return false_v; 216 | default: 217 | log_fatal( 218 | "CTrafficManager::working: failed to call " 219 | "epoll_wait-Unknown."); 220 | assert(false_v); 221 | return false_v; 222 | } 223 | } 224 | 225 | for (b4_ i = 0; i < fds; i++) { 226 | // handle accept socket 227 | if (events[i].data.fd == _listenFd) { 228 | assert(events[i].events & EPOLLIN); 229 | _addNodes(); 230 | 231 | continue; 232 | } 233 | 234 | CNode *node = (CNode *)events[i].data.u64; 235 | assert(node); 236 | 237 | if (events[i].events & EPOLLRDHUP) { 238 | log_debug("[%p %s:%u]CTrafficManager::working: node broken", 239 | node->getTransaction(), node->getIp(), node->getPort()); 240 | node->getTransaction()->over( 241 | ETransactionExitReason::CONNECTION_BROKEN, true_v); 242 | 243 | continue; 244 | } 245 | 246 | if (events[i].events & EPOLLERR) { 247 | log_debug("[%p %s:%u]CTrafficManager::working: error happens", 248 | node->getTransaction(), node->getIp(), node->getPort()); 249 | node->getTransaction()->over( 250 | ETransactionExitReason::CONNECTION_ERROR, true_v); 251 | 252 | continue; 253 | } 254 | 255 | // receive data from client 256 | if (events[i].events & EPOLLIN) { 257 | if (false_v == node->recv()) { 258 | log_debug( 259 | "[%p %s:%u]CTrafficManager::working: " 260 | "failed to receive data", 261 | node->getTransaction(), node->getIp(), node->getPort()); 262 | node->getTransaction()->over( 263 | ETransactionExitReason::CANNOT_RECV_DATA, true_v); 264 | } 265 | } 266 | } 267 | 268 | return _running; 269 | } 270 | 271 | none_ CTrafficManager::_addNodes() { 272 | for (;;) { 273 | struct sockaddr_in addr; 274 | socklen_t len = sizeof(struct sockaddr_in); 275 | b4_ clientFd = accept(_listenFd, (struct sockaddr *)&addr, &len); 276 | 277 | if (-1 == clientFd) { 278 | if (errno != EAGAIN) { 279 | log_error("CTrafficManager::addNodes: failed to accept socket"); 280 | } 281 | 282 | return; 283 | } 284 | 285 | // refuse any client if the total user has got the max value. 286 | if (0 == _res.size()) { 287 | log_crit("CTrafficManager::addNodes: cannot accept more nodes"); 288 | close(clientFd); 289 | 290 | return; 291 | } 292 | 293 | CNode *node = _res.allocate(); 294 | CNodeGroup *group = _allocateGroup(); 295 | b4_ optVal = 1; 296 | 297 | if (false_v == group->attach(node, inet_ntoa(addr.sin_addr), 298 | ntohs(addr.sin_port), clientFd)) { 299 | log_crit("CTrafficManager::addNode: no enough resource to support"); 300 | close(clientFd); 301 | 302 | return; 303 | } 304 | 305 | _setNonBlocking(clientFd); 306 | 307 | struct epoll_event ev; 308 | 309 | ev.data.u64 = (ub8_)node; 310 | ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET; 311 | 312 | if (-1 == epoll_ctl(_epollFd, EPOLL_CTL_ADD, clientFd, &ev)) { 313 | switch (errno) { 314 | case EBADF: 315 | log_fatal( 316 | "CTrafficManager::addNodes: failed to call " 317 | "epoll_ctrl-EBADF"); 318 | break; 319 | case EEXIST: 320 | log_fatal( 321 | "CTrafficManager::addNodes: failed to call " 322 | "epoll_ctrl-EEXIST"); 323 | break; 324 | case EINVAL: 325 | log_fatal( 326 | "CTrafficManager::addNodes: failed to call " 327 | "epoll_ctrl-EINVAL"); 328 | break; 329 | case ENOENT: 330 | log_fatal( 331 | "CTrafficManager::addNodes: failed to call " 332 | "epoll_ctrl-ENOENT"); 333 | break; 334 | case ENOMEM: 335 | log_fatal( 336 | "CTrafficManager::addNodes: failed to call " 337 | "epoll_ctrl-ENOMEM"); 338 | break; 339 | case ENOSPC: 340 | log_fatal( 341 | "CTrafficManager::addNodes: failed to call " 342 | "epoll_ctrl-ENOSPC"); 343 | break; 344 | case EPERM: 345 | log_fatal( 346 | "CTrafficManager::addNodes: failed to call " 347 | "epoll_ctrl-EPERM"); 348 | break; 349 | default: 350 | log_fatal( 351 | "CTrafficManager::addNodes: failed to call " 352 | "epoll_ctrl-Unknown."); 353 | } 354 | 355 | group->detach(node); 356 | 357 | return; 358 | } 359 | 360 | log_debug("[%p %s:%u]CTrafficManager::addNodes: accepted node", 361 | node->getTransaction(), node->getIp(), node->getPort()); 362 | } 363 | } 364 | 365 | none_ CTrafficManager::_delNode(CNode *node) { 366 | if (ETransactionStatus::FREE == node->getTransaction()->getStatus() || 367 | 0 == node->getFd()) { 368 | return; 369 | } 370 | 371 | log_debug("[%p %s:%u]CTrafficManager::delNode: release node", 372 | node->getTransaction(), node->getIp(), node->getPort()); 373 | 374 | if (-1 == epoll_ctl(_epollFd, EPOLL_CTL_DEL, node->getFd(), null_v)) { 375 | switch (errno) { 376 | case EBADF: 377 | log_fatal( 378 | "CTrafficManager::delNode: failed to call " 379 | "epoll_ctrl-EBADF"); 380 | break; 381 | case EEXIST: 382 | log_fatal( 383 | "CTrafficManager::delNode: failed to call " 384 | "epoll_ctrl-EEXIST"); 385 | break; 386 | case EINVAL: 387 | log_fatal( 388 | "CTrafficManager::delNode: failed to call " 389 | "epoll_ctrl-EINVAL"); 390 | break; 391 | case ENOENT: 392 | log_fatal( 393 | "CTrafficManager::delNode: failed to call " 394 | "epoll_ctrl-ENOENT"); 395 | break; 396 | case ENOMEM: 397 | log_fatal( 398 | "CTrafficManager::delNode: failed to call " 399 | "epoll_ctrl-ENOMEM"); 400 | break; 401 | case ENOSPC: 402 | log_fatal( 403 | "CTrafficManager::delNode: failed to call " 404 | "epoll_ctrl-ENOSPC"); 405 | break; 406 | case EPERM: 407 | log_fatal( 408 | "CTrafficManager::delNode: failed to call " 409 | "epoll_ctrl-EPERM"); 410 | break; 411 | default: 412 | log_fatal( 413 | "CTrafficManager::delNode: failed to call " 414 | "epoll_ctrl-Unknown."); 415 | } 416 | } 417 | 418 | close(node->getFd()); 419 | node->getGroup()->detach(node); 420 | _res.reclaim(node); 421 | } 422 | 423 | none_ CTrafficManager::recycleNode(CNode *node) { 424 | assert(null_v != node); 425 | _mutex.lock(); 426 | _nodeQueue.push(node); 427 | _mutex.unlock(); 428 | } 429 | 430 | none_ CTrafficManager::_setNonBlocking(b4_ socket) { 431 | b4_ iOpts = fcntl(socket, F_GETFL); 432 | 433 | if (0 > iOpts) { 434 | log_error( 435 | "CTrafficManager::setNonBlocking: failed to call fcntl with " 436 | "F_GETFL"); 437 | } 438 | 439 | iOpts |= O_NONBLOCK; 440 | 441 | if (0 > fcntl(socket, F_SETFL, iOpts)) { 442 | log_error( 443 | "CTrafficManager::setNonBlocking: failed to call fctntl with " 444 | "F_SETFL"); 445 | } 446 | } 447 | 448 | CNodeGroup *CTrafficManager::_allocateGroup() { 449 | if (_curGroupIndex == Config::App::NODE_GROUP_NUM) { 450 | _curGroupIndex = 0; 451 | } 452 | 453 | return &_groups[_curGroupIndex++]; 454 | } 455 | -------------------------------------------------------------------------------- /src/traffic/CTrafficManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CTrafficManager.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_TRAFFIC_MANAGER_H_ 12 | #define _C_TRAFFIC_MANAGER_H_ 13 | 14 | #include 15 | 16 | #include "../common/CMutex.h" 17 | #include "../common/CResource.h" 18 | #include "../common/CWorker.h" 19 | #include "../common/IWorkable.h" 20 | 21 | class CTransactionMangaer; 22 | class CNode; 23 | class CNodeGroup; 24 | 25 | typedef std::queue NodeQueue; 26 | 27 | class CTrafficManager : public CBase, public IWorkable { 28 | public: 29 | static CTrafficManager *instance(); 30 | static none_ destory(); 31 | 32 | none_ work(); 33 | 34 | // called by CNodeGroup thread 35 | none_ recycleNode(CNode *node); 36 | 37 | virtual bool_ working(); 38 | 39 | private: 40 | static CTrafficManager *_tm; 41 | 42 | CTrafficManager(); 43 | virtual ~CTrafficManager(); 44 | 45 | none_ _addNodes(); 46 | none_ _delNode(CNode *node); 47 | 48 | static none_ _setNonBlocking(b4_ socket); 49 | 50 | CNodeGroup *_allocateGroup(); 51 | 52 | bool_ _running; 53 | 54 | b4_ _listenFd; 55 | b4_ _epollFd; 56 | 57 | // store all nodes 58 | CResource _res; 59 | 60 | // store all groups 61 | CNodeGroup *_groups; 62 | ub4_ _curGroupIndex; 63 | 64 | CWorker _worker; 65 | 66 | // store all nodes which will be recycled 67 | NodeQueue _nodeQueue; 68 | CMutex _mutex; 69 | }; 70 | 71 | #endif // _C_TRAFFIC_MANAGER_H_ 72 | -------------------------------------------------------------------------------- /src/traffic/Message.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : Message.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _MESSAGE_H_ 12 | #define _MESSAGE_H_ 13 | 14 | #include "../config/Size.h" 15 | 16 | namespace Message { 17 | 18 | const ub4_ MSG_MAX_LENGTH = 512; 19 | 20 | //////////////////////////////////// 21 | // type values in THeader 22 | //////////////////////////////////// 23 | const ub2_ MT_CONTROL = 0x0001; 24 | const ub2_ MT_ACCOUNT = 0x0002; 25 | const ub2_ MT_SERVICE = 0x0003; 26 | 27 | const ub2_ MT_SIGN_ACK = 0x0010; 28 | const ub2_ MT_SIGN_LOG = 0x0020; 29 | const ub2_ MT_SIGN_FEE = 0x0040; 30 | const ub2_ MT_SIGN_GRP = 0x0080; 31 | 32 | //////////////////////////////////// 33 | // cmd values in THeader 34 | //////////////////////////////////// 35 | // network messages: 0xXXXX except 0x8XXX 36 | const ub2_ MC_HAND_SHAKE = 0x0001; 37 | const ub2_ MC_HEART_BEAT = 0x0002; 38 | 39 | const ub2_ MC_SEND_MSG = 0x0003; 40 | const ub2_ MC_PUSH_MSG = 0x0004; 41 | 42 | // internal messages: 0x8XXX 43 | const ub2_ MC_ON_TIMER = 0x8001; 44 | const ub2_ MC_ON_OVER = 0x8002; 45 | 46 | //////////////////////////////////// 47 | // lang values in THeader 48 | //////////////////////////////////// 49 | const ub1_ ML_CN = 0x01; 50 | const ub1_ ML_EN = 0x02; 51 | 52 | //////////////////////////////////// 53 | // PDUs 54 | //////////////////////////////////// 55 | #pragma pack(1) 56 | 57 | struct THeader { 58 | ub2_ size; 59 | ub2_ type; 60 | ub2_ cmd; 61 | ub2_ ver; 62 | ub1_ lang; 63 | ub4_ seq; 64 | ub8_ stmp; 65 | ub8_ ext; 66 | }; 67 | 68 | typedef THeader TMsg; 69 | 70 | struct TAck { 71 | ub2_ code; 72 | }; 73 | 74 | struct TPDUHandShake { 75 | THeader header; 76 | ub4_ build; 77 | ub8_ lastUpdate; 78 | c1_ sessionId[Size::SESSION_ID]; 79 | }; 80 | 81 | struct TPDUHandShakeAck { 82 | THeader header; 83 | TAck ack; 84 | }; 85 | 86 | struct TPDUHeartBeat { 87 | THeader header; 88 | }; 89 | 90 | struct TPDUHeartBeatAck { 91 | THeader header; 92 | TAck ack; 93 | }; 94 | 95 | struct TPDUSendMsg { 96 | THeader header; 97 | ub1_ dstType; // 1 - user, 2 - group 98 | ub8_ dstId; 99 | c1_ json[Size::JSON]; 100 | }; 101 | 102 | struct TPDUSendMsgAck { 103 | THeader header; 104 | TAck ack; 105 | ub8_ messageId; 106 | }; 107 | 108 | struct TPDUPushMsg { 109 | THeader header; 110 | ub1_ ornType; // 1 - user, 2 - group 111 | ub8_ ornId; 112 | ub8_ ornExtId; 113 | ub8_ messageId; 114 | c1_ json[Size::JSON]; 115 | }; 116 | 117 | struct TPDUPushMsgAck { 118 | THeader header; 119 | TAck ack; 120 | }; 121 | 122 | struct TPDUOnTimer { 123 | THeader header; 124 | ub8_ timerId; 125 | ub8_ parameter; 126 | }; 127 | 128 | struct TPDUOnOver { 129 | THeader header; 130 | b4_ reason; 131 | }; 132 | 133 | #pragma pack() 134 | } // namespace Message 135 | 136 | #endif // _MESSAGE_H_ 137 | -------------------------------------------------------------------------------- /src/traffic/SConscript: -------------------------------------------------------------------------------- 1 | env = Environment() 2 | env.Append(CCFLAGS = '-std=c++11') 3 | 4 | objs = env.Object(Glob('*.cpp')) 5 | Return('objs') -------------------------------------------------------------------------------- /src/transaction/CTransaction.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CTransaction.cpp 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #include "CTransaction.h" 12 | 13 | #include 14 | #include 15 | 16 | #include "../config/Config.h" 17 | #include "../database/CRedisOperator.h" 18 | #include "../traffic/CNode.h" 19 | #include "../traffic/CTrafficManager.h" 20 | #include "CTransactionManager.h" 21 | 22 | CTransaction::CTransaction(CNode *node) : _node(node) { 23 | _status = ETransactionStatus::FREE; 24 | _build = 0; 25 | _lastUpdate = 0; 26 | memset(_sessionId, 0, Size::SESSION_ID); 27 | _id = 0; 28 | _keepLiveTimerId = 0; 29 | _heartbeat = false_v; 30 | _seqCounter = 0; 31 | } 32 | 33 | CTransaction::~CTransaction() {} 34 | 35 | bool_ CTransaction::onAttach() { 36 | assert(0 == _build); 37 | assert(0 == _lastUpdate); 38 | assert(0 == _sessionId[0]); 39 | assert(0 == _id); 40 | assert(ETransactionStatus::FREE == _status); 41 | assert(0 == _keepLiveTimerId); 42 | assert(false_v == _heartbeat); 43 | assert(0 == _seqCounter); 44 | assert(0 == _mapSeq2Timer.size()); 45 | 46 | _status = ETransactionStatus::CONNECTED; 47 | 48 | // set timer to wait the next message coming 49 | _keepLiveTimerId = CTransactionManager::instance()->setTimer( 50 | Config::App::HEARTBEAT_INTERVAL, this, (obj_)_status, 1); 51 | 52 | if (0 == _keepLiveTimerId) { 53 | return false_v; 54 | } 55 | 56 | return true_v; 57 | } 58 | 59 | none_ CTransaction::onDetach() { 60 | assert(0 == _sessionId[0]); 61 | assert(0 == _id); 62 | assert(ETransactionStatus::OVER == _status); 63 | assert(0 == _keepLiveTimerId); 64 | assert(false_v == _heartbeat); 65 | assert(0 == _seqCounter); 66 | assert(0 == _mapSeq2Timer.size()); 67 | 68 | _status = ETransactionStatus::FREE; 69 | } 70 | 71 | bool_ CTransaction::over(ETransactionExitReason reason, bool_ useQueue) { 72 | Message::TPDUOnOver msg; 73 | 74 | msg.header.size = sizeof(Message::TPDUOnOver); 75 | msg.header.type = Message::MT_CONTROL; 76 | msg.header.cmd = Message::MC_ON_OVER; 77 | msg.header.ver = Config::App::PROTOCOL_VERSION; 78 | msg.header.lang = Message::ML_CN; 79 | msg.header.seq = 0; 80 | msg.header.stmp = CBase::now(); 81 | msg.header.ext = (ub8_)this; 82 | msg.reason = (b4_)reason; 83 | 84 | if (true_v == useQueue) { 85 | return _node->getGroup()->putMessage((const Message::TMsg *)&msg); 86 | } else { 87 | return __onStop(&msg); 88 | } 89 | } 90 | 91 | bool_ CTransaction::onMessage(const Message::TMsg *msg) { 92 | if (Message::MT_ACCOUNT == msg->type && 93 | Message::MC_HAND_SHAKE == msg->cmd) { 94 | return __onStart((Message::TPDUHandShake *)msg); 95 | } else if ((Message::MT_CONTROL | Message::MT_SIGN_ACK) == msg->type && 96 | Message::MC_HEART_BEAT == msg->cmd) { 97 | return __onHeartBeat((Message::TPDUHeartBeatAck *)msg); 98 | } else if (Message::MT_SERVICE == msg->type && 99 | Message::MC_SEND_MSG == msg->cmd) { 100 | return __onSendMsg((Message::TPDUSendMsg *)msg); 101 | } else if ((Message::MT_SERVICE | Message::MT_SIGN_ACK) == msg->type && 102 | Message::MC_PUSH_MSG == msg->cmd) { 103 | return __onPushMsg((Message::TPDUPushMsgAck *)msg); 104 | } else if (Message::MC_ON_TIMER == msg->cmd) { 105 | return __onTimer((Message::TPDUOnTimer *)msg); 106 | } else if (Message::MC_ON_OVER == msg->cmd) { 107 | return __onStop((Message::TPDUOnOver *)msg); 108 | } else { 109 | log_info( 110 | "[%p]CTransaction::onMessage: unknown message-%x-%x, current " 111 | "status-%d", 112 | this, msg->type, msg->cmd, (int)_status); 113 | return over(ETransactionExitReason::UNKNOWN_MESSAGE); 114 | } 115 | } 116 | 117 | void CTransaction::onCheck() { _node->getGroup()->ro().checkMessages(this); } 118 | 119 | bool_ CTransaction::__onStart(const Message::TPDUHandShake *msg) { 120 | log_debug("[%p]CTransaction::onStart: current status-%d", this, 121 | (int)_status); 122 | 123 | if (ETransactionStatus::CONNECTED != _status) { 124 | return over(ETransactionExitReason::WRONG_STATUS); 125 | } 126 | 127 | assert(_keepLiveTimerId); 128 | CTransactionManager::instance()->killTimer(_keepLiveTimerId); 129 | _keepLiveTimerId = 0; 130 | 131 | Message::TPDUHandShakeAck msgAck; 132 | 133 | memcpy(&msgAck, msg, sizeof(Message::THeader)); 134 | msgAck.header.size = sizeof(Message::TPDUHandShakeAck); 135 | msgAck.header.type |= Message::MT_SIGN_ACK; 136 | msgAck.ack.code = 0; 137 | 138 | if (Config::App::BASE_BUILD > msg->build) { 139 | log_notice( 140 | "[%p]CTransaction::onStart: client version with %u or above" 141 | " is necessary, now it's %u", 142 | this, Config::App::BASE_BUILD, msg->build); 143 | msgAck.ack.code = (ub2_)ETransactionExitReason::CLIENT_TOO_OLD; 144 | __send((Message::TMsg *)&msgAck, false_v); 145 | 146 | return over(ETransactionExitReason::CLIENT_TOO_OLD); 147 | } 148 | 149 | memcpy(_sessionId, msg->sessionId, Size::SESSION_ID); 150 | _id = _node->getGroup()->ro().verifyHandshake(_sessionId); 151 | 152 | if (0 == _id) { 153 | log_notice( 154 | "[%p]CTransaction::onStart: cannot find corresponding " 155 | "sessionId-%s", 156 | this, _sessionId); 157 | memset(_sessionId, 0, Size::SESSION_ID); 158 | _id = 0; 159 | msgAck.ack.code = (ub2_)ETransactionExitReason::NO_THE_SESSION_FOUND; 160 | __send((Message::TMsg *)&msgAck, false_v); 161 | 162 | return over(ETransactionExitReason::NO_THE_SESSION_FOUND); 163 | } 164 | 165 | if (false_v == CTransactionManager::instance()->registerTransaction(this)) { 166 | log_notice( 167 | "[%p]CTransaction::onStart: there is an transaction with " 168 | "the same sessionId-%s and id-%llu", 169 | this, _sessionId, _id); 170 | memset(_sessionId, 0, Size::SESSION_ID); 171 | _id = 0; 172 | msgAck.ack.code = (ub2_)ETransactionExitReason::SAME_SESSION_ID; 173 | __send((Message::TMsg *)&msgAck, false_v); 174 | 175 | return over(ETransactionExitReason::SAME_SESSION_ID); 176 | } 177 | 178 | _status = ETransactionStatus::READY; 179 | msgAck.ack.code = 0; 180 | _lastUpdate = msg->lastUpdate; 181 | 182 | if (true_v == __send((Message::TMsg *)&msgAck, false_v)) { 183 | _heartbeat = true_v; 184 | _keepLiveTimerId = CTransactionManager::instance()->setTimer( 185 | Config::App::HEARTBEAT_INTERVAL, this, (obj_)_status, 0); 186 | 187 | if (0 == _keepLiveTimerId) { 188 | return over(ETransactionExitReason::NO_MORE_TIMER); 189 | } 190 | } 191 | 192 | return true_v; 193 | } 194 | 195 | bool_ CTransaction::__onHeartBeat(const Message::TPDUHeartBeatAck *msg) { 196 | log_debug("[%p]CTransaction::onHeartBeat: current status-%d", this, 197 | (int)_status); 198 | 199 | if (ETransactionStatus::READY != _status) { 200 | return over(ETransactionExitReason::WRONG_STATUS); 201 | } 202 | 203 | _heartbeat = true_v; 204 | 205 | return true_v; 206 | } 207 | 208 | // Called by CNodeGroup thread 209 | bool_ CTransaction::__onSendMsg(const Message::TPDUSendMsg *msg) { 210 | log_debug("[%p]CTransaction::onSendMsg: current status-%d", this, 211 | (int)_status); 212 | 213 | if (ETransactionStatus::READY != _status) { 214 | return over(ETransactionExitReason::WRONG_STATUS); 215 | } 216 | 217 | Message::TPDUSendMsgAck msgAck; 218 | 219 | memcpy(&msgAck.header, &msg->header, sizeof(Message::THeader)); 220 | msgAck.header.size = sizeof(Message::TPDUSendMsgAck); 221 | msgAck.header.type |= Message::MT_SIGN_ACK; 222 | msgAck.header.stmp = CBase::now(); 223 | msgAck.ack.code = 0; 224 | 225 | if (false_v == 226 | _node->getGroup()->ro().sendMessage(this, msg, msgAck.messageId)) { 227 | msgAck.ack.code = (ub2_)ETransactionExitReason::NO_DESTINATION_FOUND; 228 | } 229 | 230 | __send((Message::TMsg *)&msgAck, false_v); 231 | 232 | return true_v; 233 | } 234 | 235 | bool_ CTransaction::handlePushMessage(ub1_ ornType, ub8_ ornId, ub8_ ornExtId, 236 | ub8_ messageId, const c1_ *json, 237 | ub2_ size, ub8_ timestamp) { 238 | Message::TPDUPushMsg msg; 239 | 240 | msg.header.size = sizeof(Message::TPDUPushMsg); 241 | msg.header.type = Message::MT_SERVICE; 242 | msg.header.cmd = Message::MC_PUSH_MSG; 243 | msg.header.ver = Config::App::PROTOCOL_VERSION; 244 | msg.header.lang = Message::ML_CN; 245 | msg.header.seq = 0; 246 | msg.header.stmp = CBase::now(); 247 | msg.header.ext = 0; 248 | msg.ornType = ornType; 249 | msg.ornId = ornId; 250 | msg.ornExtId = ornExtId; 251 | msg.messageId = messageId; 252 | memset(msg.json, 0, Size::JSON); 253 | assert(Length::JSON >= size); 254 | strncpy(msg.json, json, Length::JSON); 255 | 256 | bool_ ret = __send((Message::TMsg *)&msg, true_v); 257 | 258 | if (true_v == ret) { 259 | _lastUpdate = timestamp; 260 | } 261 | 262 | return ret; 263 | } 264 | 265 | // Called by CNodeGroup thread 266 | bool_ CTransaction::__onPushMsg(const Message::TPDUPushMsgAck *msg) { 267 | log_debug("[%p]CTransaction::onPushMsg: current status-%d", this, 268 | (int)_status); 269 | 270 | if (ETransactionStatus::READY != _status) { 271 | return over(ETransactionExitReason::WRONG_STATUS); 272 | } 273 | 274 | assert(msg->header.seq); 275 | if (0 != msg->header.seq) { 276 | MapSeq2Timer::iterator pos = _mapSeq2Timer.find(msg->header.seq); 277 | 278 | if (_mapSeq2Timer.end() != pos) { 279 | CTransactionManager::instance()->killTimer(pos->second); 280 | _mapSeq2Timer.erase(pos); 281 | } 282 | 283 | return true_v; 284 | } 285 | 286 | log_error("[%p]CTransaction::onPushMsg: no sequence found in push msg ack", 287 | this); 288 | 289 | return false_v; 290 | } 291 | 292 | bool_ CTransaction::__onTimer(const Message::TPDUOnTimer *msg) { 293 | if (_keepLiveTimerId == msg->timerId) { 294 | if (_status != (ETransactionStatus)msg->parameter) { 295 | log_debug( 296 | "[%p]CTransaction::onTimer: timer status(%d) != " 297 | "current(%d) status", 298 | this, (int)msg->parameter, (int)_status); 299 | 300 | return true_v; 301 | } 302 | 303 | if (ETransactionStatus::CONNECTED == _status) { 304 | log_debug( 305 | "[%p]CTransaction::onTimer: waiting handshake timeout, " 306 | "current status-%d", 307 | this, (int)_status); 308 | 309 | return over(ETransactionExitReason::TIME_OUT); 310 | } else if (ETransactionStatus::READY == _status) { 311 | if (false_v == _heartbeat) { 312 | log_debug( 313 | "[%p]CTransaction::onTimer: heartbeat timeout, " 314 | "current status-%d", 315 | this, (int)_status); 316 | 317 | return over(ETransactionExitReason::TIME_OUT); 318 | } else { 319 | Message::TPDUHeartBeat message; 320 | 321 | message.header.size = sizeof(Message::TPDUHeartBeat); 322 | message.header.type = Message::MT_CONTROL; 323 | message.header.cmd = Message::MC_HEART_BEAT; 324 | message.header.ver = Config::App::PROTOCOL_VERSION; 325 | message.header.lang = 1; 326 | message.header.seq = 0; 327 | 328 | __send((Message::TMsg *)&message, false_v); 329 | _heartbeat = false_v; 330 | } 331 | } else { 332 | assert(false_v); 333 | } 334 | } else { 335 | if (ETransactionStatus::READY != _status) { 336 | log_debug( 337 | "[%p]CTransaction::onTimer: seq(%u)'s status(%d) is " 338 | "expected, but now it's status(%d)", 339 | this, (unsigned int)msg->parameter, 340 | (int)ETransactionStatus::READY, (int)_status); 341 | 342 | return true_v; 343 | } 344 | 345 | ub4_ seq = (ub4_)msg->parameter; 346 | MapSeq2Timer::iterator pos = _mapSeq2Timer.find(seq); 347 | 348 | if (_mapSeq2Timer.end() != pos) { 349 | log_debug( 350 | "[%p]CTransaction::onTimer: waitting ack timeout, " 351 | "current status-%d, seq-%u", 352 | this, (int)_status, seq); 353 | 354 | return over(ETransactionExitReason::TIME_OUT); 355 | } else { 356 | assert(false_v); 357 | } 358 | } 359 | 360 | return true_v; 361 | } 362 | 363 | bool_ CTransaction::__onStop(const Message::TPDUOnOver *msg) { 364 | log_debug("[%p]CTransaction::onStop: current status-%d", this, 365 | (int)_status); 366 | _status = ETransactionStatus::OVER; 367 | 368 | switch ((ETransactionExitReason)msg->reason) { 369 | case ETransactionExitReason::WRONG_STATUS: 370 | log_debug("[%p]CTransaction::onStop: reason-%s", this, 371 | "wrong status"); 372 | break; 373 | case ETransactionExitReason::CLIENT_TOO_OLD: 374 | log_debug("[%p]CTransaction::onStop: reason-%s", this, 375 | "client too old"); 376 | break; 377 | case ETransactionExitReason::NO_THE_SESSION_FOUND: 378 | log_debug("[%p]CTransaction::onStop: reason-%s", this, 379 | "no the session found"); 380 | break; 381 | case ETransactionExitReason::SAME_SESSION_ID: 382 | log_debug("[%p]CTransaction::onStop: reason-%s", this, 383 | "same session id"); 384 | break; 385 | case ETransactionExitReason::TIME_OUT: 386 | log_debug("[%p]CTransaction::onStop: reason-%s", this, "time out"); 387 | break; 388 | case ETransactionExitReason::UNKNOWN_MESSAGE: 389 | log_debug("[%p]CTransaction::onStop: reason-%s", this, 390 | "unknown message"); 391 | break; 392 | case ETransactionExitReason::CONNECTION_BROKEN: 393 | log_debug("[%p]CTransaction::onStop: reason-%s", this, 394 | "connection broken"); 395 | break; 396 | case ETransactionExitReason::CONNECTION_ERROR: 397 | log_debug("[%p]CTransaction::onStop: reason-%s", this, 398 | "connection error"); 399 | break; 400 | case ETransactionExitReason::CANNOT_RECV_DATA: 401 | log_debug("[%p]CTransaction::onStop: reason-%s", this, 402 | "cannot recv data"); 403 | break; 404 | default: 405 | assert(false_v); 406 | } 407 | 408 | if (_keepLiveTimerId) { 409 | CTransactionManager::instance()->killTimer(_keepLiveTimerId); 410 | _keepLiveTimerId = 0; 411 | } 412 | 413 | for (MapSeq2Timer::iterator pos = _mapSeq2Timer.begin(); 414 | pos != _mapSeq2Timer.end(); pos++) { 415 | CTransactionManager::instance()->killTimer(pos->second); 416 | } 417 | 418 | _mapSeq2Timer.clear(); 419 | 420 | if (0 != _sessionId[0]) { 421 | CTransactionManager::instance()->unregisterTransaction(this); 422 | memset(_sessionId, 0, Size::SESSION_ID); 423 | } 424 | 425 | _build = 0; 426 | _lastUpdate = 0; 427 | _id = 0; 428 | _heartbeat = false_v; 429 | _seqCounter = 0; 430 | 431 | return false_v; 432 | } 433 | 434 | bool_ CTransaction::__send(Message::TMsg *msg, bool_ waitAck) { 435 | assert(null_v != msg); 436 | msg->stmp = CBase::now(); 437 | msg->ext = 0; 438 | 439 | if (true_v == waitAck) { 440 | ub4_ seq = ++_seqCounter; 441 | 442 | if (0 == seq) { 443 | seq = ++_seqCounter; 444 | } 445 | 446 | msg->seq = seq; 447 | } 448 | 449 | bool_ ret = _node->send(msg); 450 | 451 | if (true_v == ret && true_v == waitAck) { 452 | ub8_ timerId = CTransactionManager::instance()->setTimer( 453 | Config::App::HEARTBEAT_INTERVAL, this, (obj_)(ub8_)msg->seq, 1); 454 | 455 | if (timerId) { 456 | _mapSeq2Timer[msg->seq] = timerId; 457 | } else { 458 | return over(ETransactionExitReason::NO_MORE_TIMER); 459 | } 460 | } 461 | 462 | return ret; 463 | } 464 | -------------------------------------------------------------------------------- /src/transaction/CTransaction.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CTransaction.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_TRANSACTION_H_ 12 | #define _C_TRANSACTION_H_ 13 | 14 | #include 15 | 16 | #include "../common/CBase.h" 17 | #include "../traffic/Message.h" 18 | 19 | class CNode; 20 | class TTimer; 21 | 22 | // FREE->CONNECTED->READY->OVER->FREE 23 | enum class ETransactionStatus { FREE, CONNECTED, READY, OVER }; 24 | 25 | enum class ETransactionExitReason { 26 | WRONG_STATUS = 1, 27 | CLIENT_TOO_OLD, 28 | TIME_OUT, 29 | UNKNOWN_MESSAGE, 30 | NO_THE_SESSION_FOUND, 31 | SAME_SESSION_ID, 32 | CONNECTION_BROKEN, 33 | CONNECTION_ERROR, 34 | CANNOT_RECV_DATA, 35 | NO_DESTINATION_FOUND, 36 | NO_MORE_TIMER, 37 | NO_MORE_QUEUE_SPACE 38 | }; 39 | 40 | typedef std::map MapSeq2Timer; 41 | 42 | class CTransaction : public CBase { 43 | public: 44 | CTransaction(CNode *node); 45 | virtual ~CTransaction(); 46 | 47 | // Called by CTrafficManager thread 48 | bool_ onAttach(); 49 | 50 | // Called by CTrafficManager thread 51 | none_ onDetach(); 52 | 53 | // Called by CNodeGroup thread 54 | bool_ onMessage(const Message::TMsg *msg); 55 | 56 | // Called by CNodeGroup thread 57 | void onCheck(); 58 | 59 | CNode *getNode() { return _node; } 60 | 61 | ETransactionStatus getStatus() const { return _status; } 62 | 63 | const c1_ *getSessionId() const { return _sessionId; } 64 | 65 | ub8_ getId() const { return _id; } 66 | 67 | ub8_ getLastUpdate() const { return _lastUpdate; } 68 | 69 | bool_ handlePushMessage(ub1_ ornType, ub8_ ornId, ub8_ ornExtId, 70 | ub8_ messageId, const c1_ *json, ub2_ size, 71 | ub8_ timestamp); 72 | 73 | // Called by other threads except CNodeGroup thread when useQueue == true_v 74 | // Called by CNodeGroup thread when useQueue == false_v 75 | bool_ over(ETransactionExitReason reason, bool_ useQueue = false_v); 76 | 77 | protected: 78 | // Called by CNodeGroup thread 79 | bool_ __onStart(const Message::TPDUHandShake *msg); 80 | 81 | // Called by CNodeGroup thread 82 | bool_ __onHeartBeat(const Message::TPDUHeartBeatAck *msg); 83 | 84 | // Called by CNodeGroup thread 85 | bool_ __onSendMsg(const Message::TPDUSendMsg *msg); 86 | 87 | // Called by CNodeGroup thread 88 | bool_ __onPushMsg(const Message::TPDUPushMsgAck *msg); 89 | 90 | // Called by CNodeGroup thread 91 | bool_ __onTimer(const Message::TPDUOnTimer *data); 92 | 93 | // Called by CNodeGroup thread 94 | bool_ __onStop(const Message::TPDUOnOver *data); 95 | 96 | // Cassed by CNodeGroup thread 97 | bool_ __send(Message::TMsg *msg, bool_ waitAck); 98 | 99 | private: 100 | CNode *_node; 101 | 102 | ETransactionStatus _status; 103 | 104 | ub4_ _build; 105 | ub8_ _lastUpdate; 106 | c1_ _sessionId[Size::SESSION_ID]; 107 | ub8_ _id; 108 | 109 | // for heartbeat checking 110 | ub8_ _keepLiveTimerId; 111 | bool_ _heartbeat; 112 | 113 | // for message ack 114 | ub4_ _seqCounter; 115 | MapSeq2Timer _mapSeq2Timer; 116 | }; 117 | 118 | #endif // _C_TRANSACTION_H_ 119 | -------------------------------------------------------------------------------- /src/transaction/CTransactionManager.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CTransactionManager.cpp 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #include "CTransactionManager.h" 12 | 13 | #include "../common/CAutoLock.h" 14 | #include "../config/Config.h" 15 | #include "../traffic/CNode.h" 16 | #include "../traffic/CTrafficManager.h" 17 | #include "../traffic/Message.h" 18 | 19 | CTransactionManager *CTransactionManager::_tm = null_v; 20 | 21 | CTransactionManager *CTransactionManager::instance() { 22 | if (!_tm) { 23 | _tm = new CTransactionManager(); 24 | } 25 | 26 | return _tm; 27 | } 28 | 29 | CTransactionManager::CTransactionManager() 30 | : CTimerManager(Config::App::TOTAL_SUPPORT_USER_NUM * 8, 31 | Config::App::THREAD_STACK_SIZE) {} 32 | 33 | CTransactionManager::~CTransactionManager() {} 34 | 35 | none_ CTransactionManager::work() { CTrafficManager::instance()->work(); } 36 | 37 | CTransaction *CTransactionManager::findTransaction(const c1_ *sessionId) { 38 | assert(sessionId); 39 | CAutoLock al(&_mutex); 40 | 41 | TransactionMap::iterator pos = _transactionMap.find(sessionId); 42 | 43 | if (_transactionMap.end() == pos) { 44 | return null_v; 45 | } else { 46 | return pos->second; 47 | } 48 | } 49 | 50 | bool_ CTransactionManager::registerTransaction(CTransaction *transaction) { 51 | assert(transaction); 52 | CAutoLock al(&_mutex); 53 | 54 | const c1_ *sessionId = transaction->getSessionId(); 55 | ub8_ id = transaction->getId(); 56 | 57 | TransactionMap::iterator pos = _transactionMap.find(sessionId); 58 | 59 | if (_transactionMap.end() != pos) { 60 | return false_v; 61 | } 62 | 63 | _transactionMap[sessionId] = transaction; 64 | log_info( 65 | "[%p]CTransactionManager::registerTransaction: transaction " 66 | "sessionId-%s, id-%llu, total registered transactions-%lu", 67 | transaction, sessionId, id, _transactionMap.size()); 68 | 69 | return true_v; 70 | } 71 | 72 | bool_ CTransactionManager::unregisterTransaction(CTransaction *transaction) { 73 | assert(transaction); 74 | CAutoLock al(&_mutex); 75 | 76 | const c1_ *sessionId = transaction->getSessionId(); 77 | ub8_ id = transaction->getId(); 78 | 79 | if (0 == sessionId[0]) { 80 | return false_v; 81 | } 82 | 83 | TransactionMap::iterator pos = _transactionMap.find(sessionId); 84 | 85 | if (_transactionMap.end() == pos) { 86 | return false_v; 87 | } 88 | 89 | assert(transaction == pos->second); 90 | _transactionMap.erase(pos); 91 | log_info( 92 | "[%p]CTransactionManager::unregisterTransaction: transaction " 93 | "sessionId-%s, id-%llu, total registered transactions-%lu", 94 | transaction, sessionId, id, _transactionMap.size()); 95 | 96 | return true_v; 97 | } 98 | 99 | bool_ CTransactionManager::__onTimer(ub8_ timerId, obj_ parameterI, 100 | obj_ parameterII) { 101 | assert(parameterI); 102 | CTransaction *transaction = (CTransaction *)parameterI; 103 | 104 | log_debug("[%p]CTransactionManager::onTimer: timer id-%p", transaction, 105 | (obj_)timerId); 106 | 107 | Message::TPDUOnTimer msg; 108 | 109 | msg.header.size = sizeof(Message::TPDUOnTimer); 110 | msg.header.type = Message::MT_CONTROL; 111 | msg.header.cmd = Message::MC_ON_TIMER; 112 | msg.header.ver = Config::App::PROTOCOL_VERSION; 113 | msg.header.lang = 1; 114 | msg.header.seq = 0; 115 | msg.header.stmp = CBase::now(); 116 | msg.header.ext = (ub8_)transaction; 117 | msg.timerId = (ub8_)timerId; 118 | msg.parameter = (ub8_)parameterII; 119 | 120 | transaction->getNode()->getGroup()->putMessage((Message::TMsg *)&msg); 121 | 122 | return true_v; 123 | } 124 | -------------------------------------------------------------------------------- /src/transaction/CTransactionManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | ============================================================================ 3 | Name : CTransactionManager.h 4 | Author : Rafael Gu 5 | Version : 1.0 6 | Copyright : GPL 7 | Description : 8 | ============================================================================ 9 | */ 10 | 11 | #ifndef _C_TRANSACTION_MANAGER_H_ 12 | #define _C_TRANSACTION_MANAGER_H_ 13 | 14 | #include "../common/CMutex.h" 15 | #include "../common/CTimerManager.h" 16 | 17 | class CTransaction; 18 | 19 | #include 20 | 21 | #include 22 | 23 | struct StrCmp { 24 | bool operator()(const c1_ *keyLeft, const c1_ *keyRight) const { 25 | return strcmp(keyLeft, keyRight) < 0; 26 | } 27 | }; 28 | 29 | typedef std::map TransactionMap; 30 | 31 | class CTransactionManager : public CTimerManager { 32 | public: 33 | static CTransactionManager *instance(); 34 | 35 | none_ work(); 36 | 37 | CTransaction *findTransaction(const c1_ *sessionId); 38 | bool_ registerTransaction(CTransaction *transaction); 39 | bool_ unregisterTransaction(CTransaction *transaction); 40 | 41 | protected: 42 | bool_ __onTimer(ub8_ timerId, obj_ parameterI, obj_ parameterII); 43 | 44 | private: 45 | CTransactionManager(); 46 | virtual ~CTransactionManager(); 47 | 48 | static CTransactionManager *_tm; 49 | 50 | TransactionMap _transactionMap; 51 | CMutex _mutex; 52 | }; 53 | 54 | #endif // _C_TRANSACTION_MANAGER_H_ 55 | -------------------------------------------------------------------------------- /src/transaction/SConscript: -------------------------------------------------------------------------------- 1 | env = Environment() 2 | env.Append(CCFLAGS = '-std=c++11') 3 | 4 | objs = env.Object(Glob('*.cpp')) 5 | Return('objs') --------------------------------------------------------------------------------