├── LICENSE ├── README.md └── aredis.hpp /LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aredis 2 | a clean redis C++ client 3 | 4 | ```cpp 5 | redis_conn rc; 6 | if (rc.connect("127.0.0.1"/*host*/, 6379/*port*/, nullptr/*password*/, 1/*db*/)) 7 | { 8 | redis_command cmd; 9 | cmd.cmd("set", "test", 123); 10 | rc.command(cmd); 11 | resp_result res; 12 | if (rc.reply(res)) 13 | { 14 | std::cout << res.dump(); 15 | } 16 | cmd.cmd("get", "test"); 17 | rc.command(cmd); 18 | if (rc.reply(res)) 19 | { 20 | std::cout << res.dump(); 21 | } 22 | } 23 | 24 | ``` 25 | -------------------------------------------------------------------------------- /aredis.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(WIN32) || defined(_WIN32) 4 | #include 5 | #include 6 | #include 7 | #pragma comment(lib, "Ws2_32.lib") 8 | 9 | namespace aredis 10 | { 11 | struct socket_init 12 | { 13 | socket_init() 14 | { 15 | WSADATA wsaData; 16 | WSAStartup(MAKEWORD(2, 2), &wsaData); 17 | } 18 | ~socket_init() 19 | { 20 | WSACleanup(); 21 | } 22 | }; 23 | typedef SOCKET socket_type; 24 | inline void socket_close(socket_type fd) 25 | { 26 | closesocket(fd); 27 | } 28 | 29 | inline void set_nodelay(socket_type fd) 30 | { 31 | int on = 1; 32 | setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char *)&on, sizeof(on)); 33 | } 34 | } 35 | 36 | #else 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | namespace aredis 44 | { 45 | struct socket_init{}; 46 | typedef int socket_type; 47 | inline void socket_close(socket_type fd) 48 | { 49 | close(fd); 50 | } 51 | inline void set_nodelay(socket_type fd) 52 | { 53 | int on = 1; 54 | setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on)); 55 | } 56 | } 57 | 58 | #endif 59 | 60 | #include 61 | #include 62 | #include 63 | #include 64 | 65 | namespace aredis 66 | { 67 | inline void init_socket() 68 | { 69 | static socket_init sinit; 70 | } 71 | 72 | enum resp_value_type 73 | { 74 | rrt_nil, 75 | rrt_error_info, 76 | rrt_integer_value, 77 | rrt_string_value, 78 | rrt_array_value, 79 | }; 80 | 81 | enum resp_state 82 | { 83 | rs_type_read, 84 | rs_error_read, 85 | rs_error_cr, 86 | rs_integer_read, 87 | rs_integer_cr, 88 | rs_string_read, 89 | rs_string_cr, 90 | rs_bulk_len, 91 | rs_bulk_len_cr, 92 | rs_bulk_read, 93 | rs_array_read, 94 | rs_array_cr, 95 | rs_done, 96 | }; 97 | 98 | struct resp_value 99 | { 100 | union uvalue 101 | { 102 | int64_t ivalue; 103 | const char * svalue; 104 | 105 | uvalue():ivalue(0){} 106 | }values; 107 | int32_t type = rrt_nil; 108 | int32_t len = 0; 109 | std::vector arr; 110 | 111 | inline void clear() 112 | { 113 | values.ivalue = 0; 114 | type = rrt_nil; 115 | len = 0; 116 | arr.clear(); 117 | } 118 | 119 | inline bool is_nil(){ return type == rrt_nil; } 120 | 121 | inline bool is_array(){ return type == rrt_array_value; } 122 | 123 | inline bool is_error(){ return type == rrt_error_info; } 124 | 125 | inline bool is_ok() 126 | { 127 | if (type == rrt_string_value && len == 2) 128 | { 129 | if (values.svalue[0] == 'O' && values.svalue[1] == 'K') 130 | { 131 | return true; 132 | } 133 | } 134 | return false; 135 | } 136 | 137 | inline bool is_queued() 138 | { 139 | if (type == rrt_string_value && len == 6) 140 | { 141 | if (values.svalue[0] == 'Q' && values.svalue[1] == 'U' && values.svalue[2] == 'E' && values.svalue[3] == 'U' && values.svalue[4] == 'E' && values.svalue[5] == 'D') 142 | { 143 | return true; 144 | } 145 | } 146 | return false; 147 | } 148 | 149 | template 150 | inline void dump(stream_ty& os) 151 | { 152 | switch (type) 153 | { 154 | case rrt_nil: 155 | { 156 | os << "nil\n"; 157 | break; 158 | } 159 | case rrt_error_info: 160 | { 161 | os.write(values.svalue, len); 162 | os << '\n'; 163 | break; 164 | } 165 | case rrt_integer_value: 166 | { 167 | os << values.ivalue << '\n'; 168 | break; 169 | } 170 | case rrt_string_value: 171 | { 172 | os.write(values.svalue, len); 173 | os << '\n'; 174 | break; 175 | } 176 | case rrt_array_value: 177 | { 178 | for (auto& v : arr) 179 | { 180 | v.dump(os); 181 | } 182 | } 183 | } 184 | } 185 | }; 186 | 187 | enum redis_code 188 | { 189 | rc_ok, 190 | rc_incomplete, 191 | rc_server_init_error, 192 | rc_server_disconnect, 193 | rc_server_connect_fail, 194 | rc_error_endpoint, 195 | rc_auth_fail, 196 | rc_select_db_fail, 197 | rc_command_error, 198 | rc_no_resp, 199 | rc_resp_invalid, 200 | }; 201 | 202 | inline size_t i64toa(int64_t n, char * buff, size_t len) 203 | { 204 | bool sign = false; 205 | if (n < 0) 206 | { 207 | n = -n; 208 | sign = true; 209 | } 210 | size_t i = len; 211 | for (; n > 0 && i > 0; --i) 212 | { 213 | lldiv_t d = lldiv(n, 10ll); 214 | n = d.quot; 215 | buff[i - 1] = (char)d.rem + '0'; 216 | } 217 | if (sign) 218 | { 219 | --i; 220 | buff[i] = '-'; 221 | } 222 | size_t slen = len - i; 223 | if (i > 0) 224 | { 225 | memcpy(buff, buff + i, len - i); 226 | } 227 | buff[slen] = 0; 228 | return slen; 229 | } 230 | 231 | struct resp_result 232 | { 233 | redis_code error = rc_ok; 234 | resp_value val; 235 | std::string error_msg; 236 | 237 | resp_result() 238 | { 239 | } 240 | 241 | ~resp_result() 242 | { 243 | } 244 | 245 | inline void reset() 246 | { 247 | error = rc_ok; 248 | error_msg.clear(); 249 | val.clear(); 250 | } 251 | 252 | inline resp_result& operator = (resp_result& val) 253 | { 254 | error = val.error; 255 | error_msg = val.error_msg; 256 | this->val = val.val; 257 | return *this; 258 | } 259 | 260 | }; 261 | 262 | struct resp_parser 263 | { 264 | enum integer_sign 265 | { 266 | is_init, 267 | is_pos, 268 | is_neg 269 | }; 270 | struct ctx 271 | { 272 | size_t want_count = 1; 273 | size_t read_count = 0; 274 | resp_value * current = nullptr; 275 | }; 276 | std::string buff; 277 | integer_sign isign = is_init; 278 | size_t read_pos = 0; 279 | resp_state current_state = rs_type_read; 280 | resp_result res; 281 | std::vector ctxs; 282 | 283 | inline void data(const char * data, size_t len) 284 | { 285 | buff.append(data, len); 286 | } 287 | 288 | inline void clear() 289 | { 290 | reset(); 291 | buff.clear(); 292 | } 293 | 294 | inline ctx& current_ctx() 295 | { 296 | return ctxs.back(); 297 | } 298 | 299 | inline resp_value& current_value() 300 | { 301 | ctx& c = current_ctx(); 302 | if (c.current->type == rrt_array_value) 303 | { 304 | return c.current->arr[c.read_count]; 305 | } 306 | return *c.current; 307 | } 308 | 309 | inline redis_code parser_value_end() 310 | { 311 | ctx * c = ¤t_ctx(); 312 | ++c->read_count; 313 | while (c->read_count == c->want_count) 314 | { 315 | ctxs.pop_back(); 316 | if (ctxs.empty()) 317 | { 318 | current_state = rs_done; 319 | return rc_ok; 320 | } 321 | c = ¤t_ctx(); 322 | ++c->read_count; 323 | } 324 | current_state = rs_type_read; 325 | return rc_ok; 326 | } 327 | 328 | inline redis_code parser_simple_string_end() 329 | { 330 | if (read_pos < buff.size()) 331 | { 332 | if (buff[read_pos++] == '\n') 333 | { 334 | auto& val = current_value(); 335 | val.len = int(read_pos - (size_t)val.values.ivalue - 2); 336 | val.values.svalue = buff.data() + val.values.ivalue; 337 | return parser_value_end(); 338 | } 339 | else 340 | { 341 | return parser_simple_string(); 342 | } 343 | } 344 | return rc_incomplete; 345 | } 346 | 347 | inline redis_code parser_simple_string() 348 | { 349 | for (; read_pos < buff.size();) 350 | { 351 | if (buff[read_pos++] == '\r') 352 | { 353 | current_state = rs_string_cr; 354 | return parser_simple_string_end(); 355 | } 356 | } 357 | return rc_incomplete; 358 | } 359 | 360 | inline redis_code parser_error_string_end() 361 | { 362 | if (read_pos < buff.size()) 363 | { 364 | if (buff[read_pos++] == '\n') 365 | { 366 | auto& val = current_value(); 367 | val.len = int(read_pos - (size_t)val.values.ivalue - 2); 368 | res.error_msg.assign(buff.data() + val.values.ivalue, val.len); 369 | val.values.svalue = buff.data() + val.values.ivalue; 370 | return parser_value_end(); 371 | } 372 | else 373 | { 374 | return parser_error_string(); 375 | } 376 | } 377 | return rc_incomplete; 378 | } 379 | 380 | inline redis_code parser_error_string() 381 | { 382 | for (; read_pos < buff.size();) 383 | { 384 | if (buff[read_pos++] == '\r') 385 | { 386 | res.error = rc_command_error; 387 | current_state = rs_error_cr; 388 | return parser_error_string_end(); 389 | } 390 | } 391 | return rc_incomplete; 392 | } 393 | 394 | inline redis_code parser_integer_end() 395 | { 396 | if (read_pos < buff.size()) 397 | { 398 | if (buff[read_pos++] == '\n') 399 | { 400 | if (isign == is_neg) 401 | { 402 | auto& val = current_value(); 403 | val.values.ivalue = -val.values.ivalue; 404 | } 405 | isign = is_init; 406 | return parser_value_end(); 407 | } 408 | } 409 | return rc_incomplete; 410 | } 411 | 412 | inline redis_code parser_integer() 413 | { 414 | for (; read_pos < buff.size();) 415 | { 416 | char c = buff[read_pos++]; 417 | switch (c) 418 | { 419 | case '-': 420 | { 421 | if (isign == is_init) 422 | { 423 | isign = is_neg; 424 | break; 425 | } 426 | return rc_resp_invalid; 427 | } 428 | case '0': 429 | case '1': 430 | case '2': 431 | case '3': 432 | case '4': 433 | case '5': 434 | case '6': 435 | case '7': 436 | case '8': 437 | case '9': 438 | { 439 | auto& val = current_value(); 440 | if (isign == is_init) 441 | { 442 | isign = is_pos; 443 | } 444 | val.values.ivalue *= 10; 445 | val.values.ivalue += (c - '0'); 446 | break; 447 | } 448 | case '\r': 449 | { 450 | current_state = rs_integer_cr; 451 | return parser_integer_end(); 452 | } 453 | default: 454 | { 455 | return rc_resp_invalid; 456 | } 457 | } 458 | } 459 | return rc_incomplete; 460 | } 461 | 462 | inline redis_code parser_bulk_string() 463 | { 464 | size_t remain = buff.size() - read_pos; 465 | auto& val = current_value(); 466 | int32_t len = val.len; 467 | if (len < 0) 468 | { 469 | val.len = 0; 470 | val.type = rrt_nil; 471 | return parser_value_end(); 472 | } 473 | if (remain >= (size_t)(len + 2)) 474 | { 475 | if (buff[read_pos + len] == '\r' && buff[read_pos + len + 1] == '\n') 476 | { 477 | read_pos += (len + 2); 478 | val.values.svalue = buff.data() + val.values.ivalue; 479 | return parser_value_end(); 480 | } 481 | return rc_resp_invalid; 482 | } 483 | return rc_incomplete; 484 | } 485 | 486 | inline redis_code parser_bulk_end() 487 | { 488 | if (read_pos < buff.size()) 489 | { 490 | if (buff[read_pos++] == '\n') 491 | { 492 | current_state = rs_bulk_read; 493 | current_value().values.ivalue = read_pos; 494 | return parser_bulk_string(); 495 | } 496 | } 497 | return rc_incomplete; 498 | } 499 | 500 | inline redis_code parser_bulk_len() 501 | { 502 | for (; read_pos < buff.size();) 503 | { 504 | char c = buff[read_pos++]; 505 | switch (c) 506 | { 507 | case '-': 508 | { 509 | auto& val = current_value(); 510 | val.len = -1; 511 | break; 512 | } 513 | case '0': 514 | case '1': 515 | case '2': 516 | case '3': 517 | case '4': 518 | case '5': 519 | case '6': 520 | case '7': 521 | case '8': 522 | case '9': 523 | { 524 | auto& val = current_value(); 525 | if (val.len >= 0) 526 | { 527 | val.len *= 10; 528 | val.len += (c - '0'); 529 | } 530 | break; 531 | } 532 | case '\r': 533 | { 534 | current_state = rs_bulk_len_cr; 535 | return parser_bulk_end(); 536 | } 537 | default: 538 | { 539 | return rc_resp_invalid; 540 | } 541 | } 542 | } 543 | return rc_incomplete; 544 | } 545 | 546 | inline redis_code parser_array_end() 547 | { 548 | if (read_pos < buff.size()) 549 | { 550 | if (buff[read_pos++] == '\n') 551 | { 552 | current_state = rs_type_read; 553 | return rc_ok; 554 | } 555 | } 556 | return rc_incomplete; 557 | } 558 | 559 | inline redis_code parser_array_len() 560 | { 561 | for (; read_pos < buff.size();) 562 | { 563 | char c = buff[read_pos++]; 564 | switch (c) 565 | { 566 | case '0': 567 | case '1': 568 | case '2': 569 | case '3': 570 | case '4': 571 | case '5': 572 | case '6': 573 | case '7': 574 | case '8': 575 | case '9': 576 | { 577 | auto& val = current_value(); 578 | val.len *= 10; 579 | val.len += (c - '0'); 580 | break; 581 | } 582 | case '\r': 583 | { 584 | current_state = rs_array_cr; 585 | auto& c = current_ctx(); 586 | auto& cv = current_value(); 587 | if (c.current->type == rrt_nil) 588 | { 589 | c.current->type = rrt_array_value; 590 | c.want_count = cv.len; 591 | c.current->arr.resize(cv.len); 592 | } 593 | else 594 | { 595 | ctxs.emplace_back(); 596 | auto& nc = ctxs.back(); 597 | cv.type = rrt_array_value; 598 | cv.arr.resize(cv.len); 599 | nc.current = &cv; 600 | nc.want_count = cv.len; 601 | } 602 | cv.len = 0; 603 | return parser_array_end(); 604 | } 605 | default: 606 | { 607 | res.error_msg = "invalid resp"; 608 | return rc_resp_invalid; 609 | } 610 | } 611 | } 612 | return rc_incomplete; 613 | } 614 | 615 | inline redis_code parser_type() 616 | { 617 | if (read_pos >= buff.size()) 618 | { 619 | return rc_incomplete; 620 | } 621 | char ty = buff[read_pos++]; 622 | switch (ty) 623 | { 624 | case '+': 625 | { 626 | current_state = rs_string_read; 627 | auto& val = current_value(); 628 | val.type = rrt_string_value; 629 | val.values.ivalue = read_pos; 630 | return parser_simple_string(); 631 | } 632 | case '-': 633 | { 634 | current_state = rs_error_read; 635 | auto& val = current_value(); 636 | val.type = rrt_error_info; 637 | val.values.ivalue = read_pos; 638 | return parser_error_string(); 639 | } 640 | case ':': 641 | { 642 | auto& val = current_value(); 643 | val.type = rrt_integer_value; 644 | current_state = rs_integer_read; 645 | return parser_integer(); 646 | } 647 | case '$': 648 | { 649 | current_state = rs_bulk_len; 650 | auto& val = current_value(); 651 | val.type = rrt_string_value; 652 | return parser_bulk_len(); 653 | } 654 | case '*': 655 | { 656 | current_state = rs_array_read; 657 | return parser_array_len(); 658 | } 659 | } 660 | res.error_msg = "invalid resp"; 661 | return rc_resp_invalid; 662 | } 663 | 664 | inline void reset() 665 | { 666 | buff.erase(0, read_pos); 667 | ctxs.resize(1); 668 | auto& c = current_ctx(); 669 | c.current = &res.val; 670 | read_pos = 0; 671 | current_state = rs_type_read; 672 | res.reset(); 673 | } 674 | 675 | inline redis_code parser() 676 | { 677 | if (current_state == rs_done) 678 | { 679 | reset(); 680 | } 681 | while (!ctxs.empty()) 682 | { 683 | redis_code rc; 684 | switch (current_state) 685 | { 686 | case rs_type_read: 687 | { 688 | rc = parser_type(); 689 | break; 690 | } 691 | case rs_error_read: 692 | { 693 | rc = parser_error_string(); 694 | break; 695 | } 696 | case rs_error_cr: 697 | { 698 | rc = parser_error_string_end(); 699 | break; 700 | } 701 | case rs_integer_read: 702 | { 703 | rc = parser_integer(); 704 | break; 705 | } 706 | case rs_integer_cr: 707 | { 708 | rc = parser_integer_end(); 709 | break; 710 | } 711 | case rs_string_read: 712 | { 713 | rc = parser_simple_string(); 714 | break; 715 | } 716 | case rs_string_cr: 717 | { 718 | rc = parser_simple_string_end(); 719 | break; 720 | } 721 | case rs_bulk_len: 722 | { 723 | rc = parser_bulk_len(); 724 | break; 725 | } 726 | case rs_bulk_len_cr: 727 | { 728 | rc = parser_bulk_end(); 729 | break; 730 | } 731 | case rs_bulk_read: 732 | { 733 | rc = parser_bulk_string(); 734 | break; 735 | } 736 | case rs_array_read: 737 | { 738 | rc = parser_array_len(); 739 | break; 740 | } 741 | case rs_array_cr: 742 | { 743 | rc = parser_array_end(); 744 | break; 745 | } 746 | case rs_done: 747 | { 748 | break; 749 | } 750 | default: 751 | { 752 | res.error = rc_resp_invalid; 753 | res.error_msg = "invalid resp"; 754 | return rc_resp_invalid; 755 | } 756 | } 757 | if (rc != rc_ok) 758 | { 759 | res.error = rc; 760 | return rc; 761 | } 762 | if (current_state == rs_done) 763 | { 764 | res.error = rc_ok; 765 | break; 766 | } 767 | }; 768 | return rc_ok; 769 | } 770 | }; 771 | 772 | struct redis_command 773 | { 774 | enum{ head_size = 20 }; 775 | uint32_t arg_count = 0; 776 | size_t head_pos = head_size; 777 | char ltoa_buff[21]; 778 | std::string buff; 779 | 780 | redis_command() 781 | { 782 | buff.reserve(2048); 783 | buff.resize(head_size); 784 | } 785 | 786 | inline void reset() 787 | { 788 | arg_count = 0; 789 | head_pos = head_size; 790 | buff.resize(head_size); 791 | } 792 | 793 | inline redis_command& arg(const char * val , size_t len = 0) 794 | { 795 | ++arg_count; 796 | if (val == nullptr) 797 | { 798 | buff.append("$0\r\n\r\n"); 799 | } 800 | else 801 | { 802 | if (len == 0) 803 | { 804 | len = std::strlen(val); 805 | } 806 | if(len == 0) 807 | { 808 | buff.append("$0\r\n\r\n"); 809 | } 810 | else 811 | { 812 | i64toa(len, ltoa_buff, 21); 813 | buff.append(1, '$') 814 | .append(ltoa_buff) 815 | .append("\r\n") 816 | .append(val, len) 817 | .append("\r\n"); 818 | } 819 | } 820 | return *this; 821 | } 822 | 823 | inline redis_command& arg(int64_t val) 824 | { 825 | std::string v = std::to_string(val); 826 | return arg(v.data(), v.length()); 827 | } 828 | 829 | inline redis_command& arg(std::string const& val) 830 | { 831 | return arg(val.data(),val.length()); 832 | } 833 | 834 | inline void end() 835 | { 836 | if (arg_count > 0) 837 | { 838 | size_t slen = i64toa(arg_count, ltoa_buff, 21); 839 | size_t pos = head_size - slen - 3; 840 | head_pos = pos; 841 | buff[pos++] = '*'; 842 | for (size_t i = 0; i < slen; ++i) 843 | { 844 | buff[pos++] = ltoa_buff[i]; 845 | } 846 | buff[pos++] = '\r'; 847 | buff[pos++] = '\n'; 848 | } 849 | } 850 | 851 | inline redis_command& args() 852 | { 853 | return *this; 854 | } 855 | 856 | template 857 | inline redis_command& args(head const& h, targs const&... targs_) 858 | { 859 | arg(h); 860 | return args(targs_...); 861 | } 862 | 863 | template 864 | inline redis_command& cmd(const char * cmd, targs const&... targs_) 865 | { 866 | reset(); 867 | arg(cmd); 868 | return args(targs_...); 869 | } 870 | 871 | template 872 | inline redis_command& cmd(std::string const& cmd, targs const&... targs_) 873 | { 874 | reset(); 875 | arg(cmd.c_str(), cmd.length()); 876 | return args(targs_...); 877 | } 878 | 879 | inline std::string dump() 880 | { 881 | std::string str; 882 | str.reserve(buff.length() * 2); 883 | for (size_t i = head_pos; i < buff.length(); ++i) 884 | { 885 | char c = buff[i]; 886 | if (c == '\r') 887 | { 888 | str.append(1, '\\'); 889 | str.append(1, 'r'); 890 | } 891 | else if (c == '\n') 892 | { 893 | str.append(1, '\\'); 894 | str.append(1, 'n'); 895 | } 896 | else 897 | { 898 | str.append(1, c); 899 | } 900 | } 901 | return str; 902 | } 903 | }; 904 | 905 | struct batch_command 906 | { 907 | std::string buff; 908 | size_t count = 0; 909 | inline bool add(redis_command & cmd) 910 | { 911 | 912 | if (cmd.arg_count > 0) 913 | { 914 | ++count; 915 | cmd.end(); 916 | buff.append(cmd.buff.data() + cmd.head_pos, 917 | cmd.buff.length() - cmd.head_pos); 918 | return true; 919 | } 920 | return false; 921 | } 922 | 923 | inline void clear() 924 | { 925 | buff.clear(); 926 | count = 0; 927 | } 928 | }; 929 | 930 | struct redis_conn 931 | { 932 | redis_conn() 933 | { 934 | init_socket(); 935 | } 936 | 937 | ~redis_conn() 938 | { 939 | close(); 940 | } 941 | 942 | inline bool connect(const char * host = "127.0.0.1" , int port = 6379 ,const char * password = nullptr , int db = 0) 943 | { 944 | char int_buff[21]; 945 | i64toa(port, int_buff, 21); 946 | addrinfo *result = nullptr; 947 | addrinfo hints; 948 | memset(&hints, 0, sizeof(hints)); 949 | hints.ai_family = 0; 950 | hints.ai_flags = 0; 951 | hints.ai_protocol = IPPROTO_TCP; 952 | hints.ai_socktype = SOCK_STREAM; 953 | int ret = getaddrinfo(host, int_buff, &hints, &result); 954 | if (ret != 0) 955 | { 956 | parser.res.error = rc_error_endpoint; 957 | parser.res.error_msg = "endpoint error"; 958 | return false; 959 | } 960 | addrinfo *ptr = result; 961 | for (; ptr != nullptr;ptr = ptr->ai_next) 962 | { 963 | if (ptr->ai_family == AF_INET || ptr->ai_family == AF_INET6) 964 | break; 965 | } 966 | if (ptr == nullptr) 967 | { 968 | parser.res.error = rc_error_endpoint; 969 | parser.res.error_msg = "endpoint error"; 970 | return false; 971 | } 972 | memcpy((char *)&serv_addr, (char*)ptr->ai_addr, ptr->ai_addrlen); 973 | auto_connect_buff.clear(); 974 | if (password != nullptr && password[0] != 0) 975 | { 976 | do_auth = true; 977 | int len = (int)std::strlen(password); 978 | i64toa(len, int_buff, 21); 979 | auto_connect_buff.append("*2\r\n$4\r\nauth\r\n$") 980 | .append(int_buff) 981 | .append("\r\n") 982 | .append(password) 983 | .append("\r\n"); 984 | } 985 | if (db > 0) 986 | { 987 | do_switch = true; 988 | int len = (int)i64toa(db, int_buff, 21); 989 | std::string slen = std::to_string(len); 990 | auto_connect_buff.append("*2\r\n$6\r\nselect\r\n$") 991 | .append(slen) 992 | .append("\r\n") 993 | .append(int_buff) 994 | .append("\r\n"); 995 | } 996 | return connect_impl(); 997 | } 998 | 999 | inline bool reply(resp_result& res) 1000 | { 1001 | if (command_count) 1002 | { 1003 | --command_count; 1004 | return get_reply(res); 1005 | } 1006 | res.error = rc_no_resp; 1007 | res.error_msg = "no resp do command first"; 1008 | return false; 1009 | } 1010 | 1011 | inline void drop_reply() 1012 | { 1013 | resp_result res; 1014 | while (command_count) 1015 | { 1016 | --command_count; 1017 | get_reply(res); 1018 | } 1019 | } 1020 | 1021 | inline bool command(redis_command& cmd) 1022 | { 1023 | if (check_conn() == false) 1024 | { 1025 | return false; 1026 | } 1027 | if (cmd.arg_count == 0) 1028 | { 1029 | parser.res.error = rc_command_error; 1030 | parser.res.error_msg = "command error"; 1031 | return false; 1032 | } 1033 | cmd.end(); 1034 | int ret = ::send(sockfd, cmd.buff.data() + cmd.head_pos, 1035 | int(cmd.buff.length() - cmd.head_pos), 0); 1036 | if (ret < 0) 1037 | { 1038 | parser.res.error = rc_server_disconnect; 1039 | parser.res.error_msg = "server disconnect"; 1040 | close(); 1041 | return false; 1042 | } 1043 | ++command_count; 1044 | return true; 1045 | } 1046 | 1047 | inline bool command(batch_command const& cmd) 1048 | { 1049 | if (check_conn() == false) 1050 | { 1051 | return false; 1052 | } 1053 | if (cmd.buff.empty()) 1054 | { 1055 | parser.res.error = rc_command_error; 1056 | parser.res.error_msg = "command error"; 1057 | return false; 1058 | } 1059 | int ret = ::send(sockfd, cmd.buff.data(), (int)cmd.buff.length(), 0); 1060 | if (ret < 0) 1061 | { 1062 | parser.res.error = rc_server_disconnect; 1063 | parser.res.error_msg = "server disconnect"; 1064 | close(); 1065 | return false; 1066 | } 1067 | command_count += cmd.count; 1068 | return true; 1069 | } 1070 | 1071 | inline redis_code error() const{ return parser.res.error; } 1072 | 1073 | inline std::string const& error_msg() const{ return parser.res.error_msg; } 1074 | 1075 | inline bool query(aredis::redis_command& cmd, aredis::resp_result& res , size_t retry_count = 1) 1076 | { 1077 | do 1078 | { 1079 | if (this->command(cmd)) 1080 | { 1081 | if (this->reply(res) == false) 1082 | { 1083 | if (res.error == aredis::rc_server_disconnect && retry_count > 0) 1084 | { 1085 | --retry_count; 1086 | continue; 1087 | } 1088 | return false; 1089 | } 1090 | return true; 1091 | } 1092 | } while (true); 1093 | return true; 1094 | } 1095 | private: 1096 | bool do_auth = false; 1097 | bool do_switch = false; 1098 | bool init = false; 1099 | size_t command_count = 0; 1100 | socket_type sockfd = 0; 1101 | sockaddr serv_addr; 1102 | resp_parser parser; 1103 | std::string auto_connect_buff; 1104 | 1105 | inline void close() 1106 | { 1107 | if (sockfd) 1108 | { 1109 | socket_close(sockfd); 1110 | sockfd = 0; 1111 | } 1112 | } 1113 | 1114 | inline bool check_conn() 1115 | { 1116 | if (init == false) 1117 | { 1118 | parser.res.error = rc_server_init_error; 1119 | parser.res.error_msg = "server init error"; 1120 | return false; 1121 | } 1122 | if (sockfd == 0) 1123 | { 1124 | if (connect_impl() == false) 1125 | { 1126 | return false; 1127 | } 1128 | } 1129 | return true; 1130 | } 1131 | 1132 | inline bool connect_impl() 1133 | { 1134 | close(); 1135 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 1136 | if (::connect(sockfd, &serv_addr, sizeof(serv_addr)) < 0) 1137 | { 1138 | parser.res.error = rc_server_connect_fail; 1139 | parser.res.error_msg = "could not connect to server"; 1140 | close(); 1141 | return false; 1142 | } 1143 | set_nodelay(sockfd); 1144 | if (!auto_connect_buff.empty()) 1145 | { 1146 | int ret = ::send(sockfd, auto_connect_buff.data(), (int)auto_connect_buff.length(), 0); 1147 | if (ret < 0) 1148 | { 1149 | parser.res.error = rc_server_disconnect; 1150 | parser.res.error_msg = "server disconnect"; 1151 | close(); 1152 | return false; 1153 | } 1154 | parser.clear(); 1155 | command_count = 0; 1156 | resp_result res; 1157 | if (do_auth) 1158 | { 1159 | if (get_reply(res) == false) 1160 | { 1161 | return false; 1162 | } 1163 | if (!res.val.is_ok()) 1164 | { 1165 | parser.res.error = rc_auth_fail; 1166 | parser.res.error_msg = "auth failed"; 1167 | return false; 1168 | } 1169 | } 1170 | if (do_switch) 1171 | { 1172 | if (get_reply(res) == false) 1173 | { 1174 | return false; 1175 | } 1176 | if (!res.val.is_ok()) 1177 | { 1178 | parser.res.error = rc_select_db_fail; 1179 | parser.res.error_msg = "select db failed"; 1180 | return false; 1181 | } 1182 | } 1183 | } 1184 | init = true; 1185 | return true; 1186 | } 1187 | 1188 | inline bool get_reply(resp_result& res) 1189 | { 1190 | if (sockfd == 0) 1191 | { 1192 | res.error = rc_server_disconnect; 1193 | res.error_msg = "server disconnect"; 1194 | res.val.clear(); 1195 | return false; 1196 | } 1197 | char buffer[16384]; 1198 | if (sockfd) 1199 | { 1200 | parser.reset(); 1201 | do 1202 | { 1203 | redis_code rc = parser.parser(); 1204 | if (rc == rc_incomplete) 1205 | { 1206 | int ret = ::recv(sockfd, buffer, sizeof(buffer), 0); 1207 | if (ret <= 0) 1208 | { 1209 | close(); 1210 | parser.res.error = rc_server_disconnect; 1211 | parser.res.error_msg = "server disconnect"; 1212 | break; 1213 | } 1214 | else 1215 | { 1216 | parser.data(buffer, ret); 1217 | } 1218 | continue; 1219 | } 1220 | else 1221 | { 1222 | parser.res.error = rc; 1223 | if (rc == rc_ok) 1224 | { 1225 | res = parser.res; 1226 | return true; 1227 | } 1228 | } 1229 | } while (true); 1230 | } 1231 | res.error = parser.res.error; 1232 | res.error_msg = parser.res.error_msg; 1233 | res.val.clear(); 1234 | return false; 1235 | } 1236 | }; 1237 | } 1238 | --------------------------------------------------------------------------------