RedisClient.cs 52.1 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107
using HslCommunication.Core;
using HslCommunication.Core.IMessage;
using HslCommunication.Core.Net;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;

namespace HslCommunication.Enthernet.Redis
{
    /// <summary>
    /// 这是一个redis的客户端类,支持读取,写入,发布订阅,但是不支持订阅,如果需要订阅,请使用另一个类
    /// </summary>
    public class RedisClient : NetworkDoubleBase<HslMessage, RegularByteTransform>
    {
        #region Constructor

        /// <summary>
        /// 实例化一个客户端的对象,用于和服务器通信
        /// </summary>
        /// <param name="ipAddress">服务器的ip地址</param>
        /// <param name="port">服务器的端口号</param>
        /// <param name="password">密码,如果服务器没有设置,密码设置为null</param>
        public RedisClient(string ipAddress, int port, string password)
        {
            IpAddress = ipAddress;
            Port = port;
            ReceiveTimeOut = 30000;
            this.password = password;
        }

        /// <summary>
        /// 实例化一个客户端对象,需要手动指定Ip地址和端口
        /// </summary>
        /// <param name="password">密码,如果服务器没有设置,密码设置为null</param>
        public RedisClient(string password)
        {
            ReceiveTimeOut = 30000;
            this.password = password;
        }

        #endregion

        #region Override

        /// <summary>
        /// 如果设置了密码,对密码进行验证
        /// </summary>
        /// <param name="socket">网络的套接字服务</param>
        /// <returns>是否成功的对象</returns>
        protected override OperateResult InitializationOnConnect(Socket socket)
        {
            if (!string.IsNullOrEmpty(this.password))
            {
                byte[] command = RedisHelper.PackStringCommand(new string[] { "AUTH", this.password });

                OperateResult<byte[]> read = ReadFromCoreServer(socket, command);
                if (!read.IsSuccess) return OperateResult.CreateFailedResult<string>(read);

                string msg = Encoding.UTF8.GetString(read.Content);
                if (!msg.StartsWith("+")) return new OperateResult<string>(msg);

                return OperateResult.CreateSuccessResult(msg.Substring(1).TrimEnd('\r', '\n'));
            }
            return base.InitializationOnConnect(socket);
        }

        /// <summary>
        /// 在其他指定的套接字上,使用报文来通讯,传入需要发送的消息,返回一条完整的数据指令
        /// </summary>
        /// <param name="socket">指定的套接字</param>
        /// <param name="send">发送的完整的报文信息</param>
        /// <remarks>
        /// 无锁的基于套接字直接进行叠加协议的操作。
        /// </remarks>
        /// <example>
        /// 假设你有一个自己的socket连接了设备,本组件可以直接基于该socket实现modbus读取,三菱读取,西门子读取等等操作,前提是该服务器支持多协议,虽然这个需求听上去比较变态,但本组件支持这样的操作。
        /// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Core\NetworkDoubleBase.cs" region="ReadFromCoreServerExample1" title="ReadFromCoreServer示例" />
        /// </example>
        /// <returns>接收的完整的报文信息</returns>
        public override OperateResult<byte[]> ReadFromCoreServer(Socket socket, byte[] send)
        {
            OperateResult sendResult = Send(socket, send);
            if (!sendResult.IsSuccess) return OperateResult.CreateFailedResult<byte[]>(sendResult);

            string tmp = BasicFramework.SoftBasic.ByteToHexString(send, ' ');
            // 接收超时时间大于0时才允许接收远程的数据
            if (ReceiveTimeOut < 0) return OperateResult.CreateSuccessResult(new byte[0]);

            // 接收数据信息
            return RedisHelper.ReceiveCommand(socket);
        }

        #endregion

        #region Customer

        /// <summary>
        /// 自定义的指令交互方法,该指令用空格分割,举例:LTRIM AAAAA 0 999 就是收缩列表,GET AAA 就是获取键值,需要对返回的数据进行二次分析
        /// </summary>
        /// <param name="command">举例:LTRIM AAAAA 0 999 就是收缩列表,GET AAA 就是获取键值</param>
        /// <returns>从服务器返回的结果数据对象</returns>
        public OperateResult<string> ReadCustomer(string command)
        {
            byte[] byteCommand = RedisHelper.PackStringCommand(command.Split(' '));

            OperateResult<byte[]> read = ReadFromCoreServer(byteCommand);
            if (!read.IsSuccess) return OperateResult.CreateFailedResult<string>(read);

            return OperateResult.CreateSuccessResult(Encoding.UTF8.GetString(read.Content));
        }

        #endregion

        #region Base Operate

        /// <summary>
        /// 向服务器请求指定,并返回数字的结果对象
        /// </summary>
        /// <param name="commands">命令数组</param>
        /// <returns>数字的结果对象</returns>
        public OperateResult<int> OperateNumberFromServer(string[] commands)
        {
            byte[] command = RedisHelper.PackStringCommand(commands);

            OperateResult<byte[]> read = ReadFromCoreServer(command);
            if (!read.IsSuccess) return OperateResult.CreateFailedResult<int>(read);

            string msg = Encoding.UTF8.GetString(read.Content);
            if (!msg.StartsWith(":")) return new OperateResult<int>(msg);

            return RedisHelper.GetNumberFromCommandLine(read.Content);
        }

        /// <summary>
        /// 向服务器请求指令,并返回long数字的结果对象
        /// </summary>
        /// <param name="commands">命令数组</param>
        /// <returns>long数字的结果对象</returns>
        public OperateResult<long> OperateLongNumberFromServer(string[] commands)
        {
            byte[] command = RedisHelper.PackStringCommand(commands);

            OperateResult<byte[]> read = ReadFromCoreServer(command);
            if (!read.IsSuccess) return OperateResult.CreateFailedResult<long>(read);

            string msg = Encoding.UTF8.GetString(read.Content);
            if (!msg.StartsWith(":")) return new OperateResult<long>(msg);

            return RedisHelper.GetLongNumberFromCommandLine(read.Content);
        }

        /// <summary>
        /// 向服务器请求指令,并返回字符串的结果对象
        /// </summary>
        /// <param name="commands">命令数组</param>
        /// <returns>字符串的结果对象</returns>
        public OperateResult<string> OperateStringFromServer(string[] commands)
        {
            byte[] command = RedisHelper.PackStringCommand(commands);

            OperateResult<byte[]> read = ReadFromCoreServer(command);
            if (!read.IsSuccess) return OperateResult.CreateFailedResult<string>(read);

            return RedisHelper.GetStringFromCommandLine(read.Content);
        }

        /// <summary>
        /// 向服务器请求指令,并返回字符串数组的结果对象
        /// </summary>
        /// <param name="commands">命令数组</param>
        /// <returns>字符串数组的结果对象</returns>
        public OperateResult<string[]> OperateStringsFromServer(string[] commands)
        {
            byte[] command = RedisHelper.PackStringCommand(commands);

            OperateResult<byte[]> read = ReadFromCoreServer(command);
            if (!read.IsSuccess) return OperateResult.CreateFailedResult<string[]>(read);

            return RedisHelper.GetStringsFromCommandLine(read.Content);
        }

        /// <summary>
        /// 向服务器请求指令,并返回状态的结果对象,通常用于写入的判断,或是请求类型的判断
        /// </summary>
        /// <param name="commands">命令数组</param>
        /// <returns>是否成功的结果对象</returns>
        public OperateResult<string> OperateStatusFromServer(string[] commands)
        {
            byte[] command = RedisHelper.PackStringCommand(commands);

            OperateResult<byte[]> read = ReadFromCoreServer(command);
            if (!read.IsSuccess) return OperateResult.CreateFailedResult<string>(read);

            string msg = Encoding.UTF8.GetString(read.Content);
            if (!msg.StartsWith("+")) return new OperateResult<string>(msg);

            return OperateResult.CreateSuccessResult(msg.Substring(1).TrimEnd('\r', '\n'));
        }

        #endregion

        #region Key Operate

        /// <summary>
        /// 删除给定的一个或多个 key 。不存在的 key 会被忽略。
        /// </summary>
        /// <param name="keys">关键字</param>
        /// <returns>被删除 key 的数量。</returns>
        public OperateResult<int> DeleteKey(string[] keys)
        {
            List<string> list = new List<string>();
            list.Add("DEL");
            list.AddRange(keys);

            return OperateNumberFromServer(list.ToArray());
        }

        /// <summary>
        /// 删除给定的一个或多个 key 。不存在的 key 会被忽略。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <returns>被删除 key 的数量。</returns>
        public OperateResult<int> DeleteKey(string key)
        {
            return DeleteKey(new string[] { key });
        }

        /// <summary>
        /// 检查给定 key 是否存在。若 key 存在,返回 1 ,否则返回 0 。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <returns>若 key 存在,返回 1 ,否则返回 0 。</returns>
        public OperateResult<int> ExistsKey(string key)
        {
            return OperateNumberFromServer(new string[] { "EXISTS", key });
        }

        /// <summary>
        /// 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。设置成功返回 1 。当 key 不存在或者不能为 key 设置生存时间时,返回 0 。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <returns>
        /// 设置成功返回 1 。当 key 不存在或者不能为 key 设置生存时间时,返回 0 。
        /// </returns>
        public OperateResult<int> ExpireKey(string key)
        {
            return OperateNumberFromServer(new string[] { "EXPIRE", key });
        }

        /// <summary>
        /// 查找所有符合给定模式 pattern 的 key 。
        /// * 匹配数据库中所有 key。
        /// h?llo 匹配 hello , hallo 和 hxllo 等。
        /// h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo 。
        /// </summary>
        /// <param name="pattern">给定模式</param>
        /// <returns>符合给定模式的 key 列表。</returns>
        public OperateResult<string[]> ReadAllKeys(string pattern)
        {
            return OperateStringsFromServer(new string[] { "KEYS", pattern });
        }

        /// <summary>
        /// 将当前数据库的 key 移动到给定的数据库 db 当中。
        /// 如果当前数据库(源数据库)和给定数据库(目标数据库)有相同名字的给定 key ,或者 key 不存在于当前数据库,那么 MOVE 没有任何效果。
        /// 因此,也可以利用这一特性,将 MOVE 当作锁(locking)原语(primitive)。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="db">数据块</param>
        /// <returns>是否移动成功</returns>
        public OperateResult MoveKey(string key, int db)
        {
            return OperateStatusFromServer(new string[] { "MOVE", key, db.ToString() });
        }

        /// <summary>
        /// 移除给定 key 的生存时间,将这个 key 从『易失的』(带生存时间 key )转换成『持久的』(一个不带生存时间、永不过期的 key )。
        /// 当生存时间移除成功时,返回 1 .
        /// 如果 key 不存在或 key 没有设置生存时间,返回 0 。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <returns>
        /// 当生存时间移除成功时,返回 1 .
        /// 如果 key 不存在或 key 没有设置生存时间,返回 0 。
        /// </returns>
        public OperateResult<int> PersistKey(string key)
        {
            return OperateNumberFromServer(new string[] { "PERSIST", key });
        }

        /// <summary>
        /// 从当前数据库中随机返回(不删除)一个 key 。
        /// 当数据库不为空时,返回一个 key 。
        /// 当数据库为空时,返回 nil 。
        /// </summary>
        /// <returns>
        /// 当数据库不为空时,返回一个 key 。
        /// 当数据库为空时,返回 nil 。
        /// </returns>
        public OperateResult<string> ReadRandomKey()
        {
            return OperateStringFromServer(new string[] { "RANDOMKEY" });
        }

        /// <summary>
        /// 将 key 改名为 newkey 。
        /// 当 key 和 newkey 相同,或者 key 不存在时,返回一个错误。
        /// 当 newkey 已经存在时, RENAME 命令将覆盖旧值。
        /// </summary>
        /// <param name="key1">旧的key</param>
        /// <param name="key2">新的key</param>
        /// <returns>
        /// 改名成功时提示 OK ,失败时候返回一个错误。
        /// </returns>
        public OperateResult RenameKey(string key1, string key2)
        {
            return OperateStatusFromServer(new string[] { "RENAME", key1, key2 });
        }

        /// <summary>
        /// 返回 key 所储存的值的类型。none (key不存在),string (字符串),list (列表),set (集合),zset (有序集),hash (哈希表)
        /// </summary>
        /// <param name="key">关键字</param>
        /// <returns>类型</returns>
        public OperateResult<string> ReadKeyType(string key)
        {
            return OperateStatusFromServer(new string[] { "TYPE", key });
        }



        #endregion

        #region String Operate

        /// <summary>
        /// 如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。
        /// 如果 key 不存在, APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样。
        /// 返回追加 value 之后, key 中字符串的长度。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">数值</param>
        /// <returns>
        /// 追加 value 之后, key 中字符串的长度。
        /// </returns>
        public OperateResult<int> AppendKey(string key, string value)
        {
            return OperateNumberFromServer(new string[] { "APPEND", key, value });
        }

        /// <summary>
        /// 将 key 中储存的数字值减一。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。
        /// 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
        /// 本操作的值限制在 64 位(bit)有符号数字表示之内。
        /// 返回执行 DECR 命令之后 key 的值。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <returns>执行 DECR 命令之后 key 的值。</returns>
        public OperateResult<long> DecrementKey(string key)
        {
            return OperateLongNumberFromServer(new string[] { "DECR", key });
        }

        /// <summary>
        /// 将 key 所储存的值减去减量 decrement 。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。
        /// 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
        /// 本操作的值限制在 64 位(bit)有符号数字表示之内。
        /// 返回减去 decrement 之后, key 的值。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">操作的值</param>
        /// <returns>返回减去 decrement 之后, key 的值。</returns>
        public OperateResult<long> DecrementKey(string key, long value)
        {
            return OperateLongNumberFromServer(new string[] { "DECRBY", key, value.ToString() });
        }

        /// <summary>
        /// 返回 key 所关联的字符串值。如果 key 不存在那么返回特殊值 nil 。
        /// 假如 key 储存的值不是字符串类型,返回一个错误,因为 GET 只能用于处理字符串值。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <returns>当 key 不存在时,返回 nil ,否则,返回 key 的值。</returns>
        public OperateResult<string> ReadKey(string key)
        {
            return OperateStringFromServer(new string[] { "GET", key });
        }

        /// <summary>
        /// 返回 key 中字符串值的子字符串,字符串的截取范围由 start 和 end 两个偏移量决定(包括 start 和 end 在内)。
        /// 负数偏移量表示从字符串最后开始计数, -1 表示最后一个字符, -2 表示倒数第二个,以此类推。
        /// 返回截取得出的子字符串。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="start">截取开始的位置</param>
        /// <param name="end">截取结束的位置</param>
        /// <returns>返回截取得出的子字符串。</returns>
        public OperateResult<string> ReadKeyRange(string key, int start, int end)
        {
            return OperateStringFromServer(new string[] { "GETRANGE", key, start.ToString(), end.ToString() });
        }

        /// <summary>
        /// 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。当 key 存在但不是字符串类型时,返回一个错误。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">新的值</param>
        /// <returns>返回给定 key 的旧值。当 key 没有旧值时,也即是, key 不存在时,返回 nil 。</returns>
        public OperateResult<string> ReadAndWriteKey(string key, string value)
        {
            return OperateStringFromServer(new string[] { "GETSET", key, value });
        }

        /// <summary>
        /// 将 key 中储存的数字值增一。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
        /// 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
        /// 返回执行 INCR 命令之后 key 的值。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <returns>返回执行 INCR 命令之后 key 的值。</returns>
        public OperateResult<long> IncrementKey(string key)
        {
            return OperateLongNumberFromServer(new string[] { "INCR", key });
        }

        /// <summary>
        /// 将 key 所储存的值加上增量 increment 。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
        /// 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">增量数据</param>
        /// <returns>加上 increment 之后, key 的值。</returns>
        public OperateResult<long> IncrementKey(string key, long value)
        {
            return OperateLongNumberFromServer(new string[] { "INCRBY", key, value.ToString() });
        }

        /// <summary>
        /// 将 key 所储存的值加上增量 increment 。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBYFLOAT 操作。
        /// 如果命令执行成功,那么 key 的值会被更新为(执行加法之后的)新值,并且新值会以字符串的形式返回给调用者
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">增量数据</param>
        /// <returns>执行命令之后 key 的值。</returns>
        public OperateResult<string> IncrementKey(string key, float value)
        {
            return OperateStringFromServer(new string[] { "INCRBYFLOAT", key, value.ToString() });
        }


        /// <summary>
        /// 返回所有(一个或多个)给定 key 的值。
        /// 如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 null 。因此,该命令永不失败。
        /// </summary>
        /// <param name="keys">关键字数组</param>
        /// <returns>一个包含所有给定 key 的值的列表。</returns>
        public OperateResult<string[]> ReadKey(string[] keys)
        {
            List<string> list = new List<string>();
            list.Add("MGET");
            list.AddRange(keys);

            return OperateStringsFromServer(list.ToArray());
        }

        /// <summary>
        /// 同时设置一个或多个 key-value 对。
        /// 如果某个给定 key 已经存在,那么 MSET 会用新值覆盖原来的旧值,如果这不是你所希望的效果,请考虑使用 MSETNX 命令:它只会在所有给定 key 都不存在的情况下进行设置操作。
        /// </summary>
        /// <param name="keys">关键字数组</param>
        /// <param name="values">值数组</param>
        /// <returns>总是返回 OK (因为 MSET 不可能失败)</returns>
        public OperateResult WriteKey(string[] keys, string[] values)
        {
            if (keys == null) throw new ArgumentNullException("keys");
            if (values == null) throw new ArgumentNullException("values");
            if (keys.Length != values.Length) throw new ArgumentException("Two arguement not same length");

            List<string> list = new List<string>();
            list.Add("MSET");
            for (int i = 0; i < keys.Length; i++)
            {
                list.Add(keys[i]);
                list.Add(values[i]);
            }

            return OperateStatusFromServer(list.ToArray());
        }

        /// <summary>
        /// 将字符串值 value 关联到 key 。
        /// 如果 key 已经持有其他值, SET 就覆写旧值,无视类型。
        /// 对于某个原本带有生存时间(TTL)的键来说, 当 SET 命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">数据值</param>
        /// <returns> SET 在设置操作成功完成时,才返回 OK 。</returns>
        public OperateResult WriteKey(string key, string value)
        {
            return OperateStatusFromServer(new string[] { "SET", key, value });
        }

        /// <summary>
        /// 将字符串值 value 关联到 key 。并发布一个订阅的频道数据,都成功时,才返回成功
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">数据值</param>
        /// <returns>是否成功的结果对象</returns>
        public OperateResult WriteAndPublishKey(string key, string value)
        {
            OperateResult write = WriteKey(key, value);
            if (!write.IsSuccess) return write;

            return Publish(key, value);
        }

        /// <summary>
        /// 将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位)。如果 key 已经存在, SETEX 命令将覆写旧值。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">数值</param>
        /// <param name="seconds">生存时间,单位秒</param>
        /// <returns>设置成功时返回 OK 。当 seconds 参数不合法时,返回一个错误。</returns>
        public OperateResult WriteExpireKey(string key, string value, long seconds)
        {
            return OperateStatusFromServer(new string[] { "SETEX", key, seconds.ToString(), value });
        }

        /// <summary>
        /// 将 key 的值设为 value ,当且仅当 key 不存在。若给定的 key 已经存在,则 SETNX 不做任何动作。设置成功,返回 1 。设置失败,返回 0 。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">数据值</param>
        /// <returns>设置成功,返回 1 。设置失败,返回 0 。</returns>
        public OperateResult<int> WriteKeyIfNotExists(string key, string value)
        {
            return OperateNumberFromServer(new string[] { "SETNX", key, value });
        }

        /// <summary>
        /// 用 value 参数覆写(overwrite)给定 key 所储存的字符串值,从偏移量 offset 开始。不存在的 key 当作空白字符串处理。返回被 SETRANGE 修改之后,字符串的长度。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">数值</param>
        /// <param name="offset">起始的偏移量</param>
        /// <returns>被 SETRANGE 修改之后,字符串的长度。</returns>
        public OperateResult<int> WriteKeyRange(string key, string value, int offset)
        {
            return OperateNumberFromServer(new string[] { "SETRANGE", key, offset.ToString(), value });
        }

        /// <summary>
        /// 返回 key 所储存的字符串值的长度。当 key 储存的不是字符串值时,返回一个错误。返回符串值的长度。当 key 不存在时,返回 0 。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <returns>字符串值的长度。当 key 不存在时,返回 0 。</returns>
        public OperateResult<int> ReadKeyLength(string key)
        {
            return OperateNumberFromServer(new string[] { "STRLEN", key });
        }

        #endregion

        #region List Operate

        /// <summary>
        /// 将值 value 插入到列表 key 当中,位于值 pivot 之前。
        /// 当 pivot 不存在于列表 key 时,不执行任何操作。
        /// 当 key 不存在时, key 被视为空列表,不执行任何操作。
        /// 如果 key 不是列表类型,返回一个错误。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">数值</param>
        /// <param name="pivot">原先的值</param>
        /// <returns>
        /// 如果命令执行成功,返回插入操作完成之后,列表的长度。
        /// 如果没有找到 pivot ,返回 -1 。
        /// 如果 key 不存在或为空列表,返回 0 。
        /// </returns>
        public OperateResult<int> ListInsertBefore(string key, string value, string pivot)
        {
            return OperateNumberFromServer(new string[] { "LINSERT", key, "BEFORE", pivot, value });
        }

        /// <summary>
        /// 将值 value 插入到列表 key 当中,位于值 pivot 之后。
        /// 当 pivot 不存在于列表 key 时,不执行任何操作。
        /// 当 key 不存在时, key 被视为空列表,不执行任何操作。
        /// 如果 key 不是列表类型,返回一个错误。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">数值</param>
        /// <param name="pivot">原先的值</param>
        /// <returns>
        /// 如果命令执行成功,返回插入操作完成之后,列表的长度。
        /// 如果没有找到 pivot ,返回 -1 。
        /// 如果 key 不存在或为空列表,返回 0 。
        /// </returns>
        public OperateResult<int> ListInsertAfter(string key, string value, string pivot)
        {
            return OperateNumberFromServer(new string[] { "LINSERT", key, "AFTER", pivot, value });
        }

        /// <summary>
        /// 返回列表 key 的长度。如果 key 不存在,则 key 被解释为一个空列表,返回 0 .如果 key 不是列表类型,返回一个错误。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <returns>列表 key 的长度。</returns>
        public OperateResult<int> GetListLength(string key)
        {
            return OperateNumberFromServer(new string[] { "LLEN", key });
        }

        /// <summary>
        /// 返回列表 key 中,下标为 index 的元素。下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
        /// 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。如果 key 不是列表类型,返回一个错误。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="index">索引位置</param>
        /// <returns>列表中下标为 index 的元素。如果 index 参数的值不在列表的区间范围内(out of range),返回 nil 。</returns>
        public OperateResult<string> ReadListByIndex(string key, long index)
        {
            return OperateStringFromServer(new string[] { "LINDEX", key, index.ToString() });
        }

        /// <summary>
        /// 移除并返回列表 key 的头元素。列表的头元素。当 key 不存在时,返回 nil 。
        /// </summary>
        /// <param name="key">关键字信息</param>
        /// <returns>列表的头元素。</returns>
        public OperateResult<string> ListLeftPop(string key)
        {
            return OperateStringFromServer(new string[] { "LPOP", key });
        }

        /// <summary>
        /// 将一个或多个值 value 插入到列表 key 的表头,如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。当 key 存在但不是列表类型时,返回一个错误。返回执行 LPUSH 命令后,列表的长度。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">值</param>
        /// <returns>执行 LPUSH 命令后,列表的长度。</returns>
        public OperateResult<int> ListLeftPush(string key, string value)
        {
            return ListLeftPush(key, new string[] { value });
        }

        /// <summary>
        /// 将一个或多个值 value 插入到列表 key 的表头,如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。当 key 存在但不是列表类型时,返回一个错误。返回执行 LPUSH 命令后,列表的长度。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="values">值</param>
        /// <returns>执行 LPUSH 命令后,列表的长度。</returns>
        public OperateResult<int> ListLeftPush(string key, string[] values)
        {
            List<string> list = new List<string>();
            list.Add("LPUSH");
            list.Add(key);
            list.AddRange(values);

            return OperateNumberFromServer(list.ToArray());
        }

        /// <summary>
        /// 将值 value 插入到列表 key 的表头,当且仅当 key 存在并且是一个列表。和 LPUSH 命令相反,当 key 不存在时, LPUSHX 命令什么也不做。
        /// 返回LPUSHX 命令执行之后,表的长度。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">值</param>
        /// <returns>是否插入数据成功</returns>
        public OperateResult<int> ListLeftPushX(string key, string value)
        {
            return OperateNumberFromServer(new string[] { "LPUSHX", key, value });
        }

        /// <summary>
        /// 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。
        /// 下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
        /// 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
        /// 返回一个列表,包含指定区间内的元素。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="start">开始的索引</param>
        /// <param name="stop">结束的索引</param>
        /// <returns>返回一个列表,包含指定区间内的元素。</returns>
        public OperateResult<string[]> ListRange(string key, long start, long stop)
        {
            return OperateStringsFromServer(new string[] { "LRANGE", key, start.ToString(), stop.ToString() });
        }

        /// <summary>
        /// 根据参数 count 的值,移除列表中与参数 value 相等的元素。count 的值可以是以下几种:
        /// count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。
        /// count &lt; 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。
        /// count = 0 : 移除表中所有与 value 相等的值。
        /// 返回被移除的数量。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="count">移除参数</param>
        /// <param name="value">匹配的值</param>
        /// <returns>被移除元素的数量。因为不存在的 key 被视作空表(empty list),所以当 key 不存在时, LREM 命令总是返回 0 。</returns>
        public OperateResult<int> ListRemoveElementMatch(string key, long count, string value)
        {
            return OperateNumberFromServer(new string[] { "LREM", key, count.ToString(), value });
        }

        /// <summary>
        /// 设置数组的某一个索引的数据信息,当 index 参数超出范围,或对一个空列表( key 不存在)进行 LSET 时,返回一个错误。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="index">索引位置</param>
        /// <param name="value">值</param>
        /// <returns>操作成功返回 ok ,否则返回错误信息。</returns>
        public OperateResult ListSet(string key, long index, string value)
        {
            return OperateStatusFromServer(new string[] { "LSET", key.ToString(), index.ToString(), value });
        }

        /// <summary>
        /// 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
        /// 举个例子,执行命令 LTRIM list 0 2 ,表示只保留列表 list 的前三个元素,其余元素全部删除。
        /// 下标( index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
        /// 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
        /// 当 key 不是列表类型时,返回一个错误。
        /// </summary>
        /// <param name="key">关键字信息</param>
        /// <param name="start">起始的索引信息</param>
        /// <param name="end">结束的索引信息</param>
        /// <returns>操作成功返回 ok ,否则返回错误信息。</returns>
        public OperateResult ListTrim(string key, long start, long end)
        {
            return OperateStatusFromServer(new string[] { "LTRIM", key, start.ToString(), end.ToString() });
        }

        /// <summary>
        /// 移除并返回列表 key 的尾元素。当 key 不存在时,返回 nil 。
        /// </summary>
        /// <param name="key">关键字信息</param>
        /// <returns>列表的尾元素。</returns>
        public OperateResult<string> ListRightPop(string key)
        {
            return OperateStringFromServer(new string[] { "RPOP", key });
        }

        /// <summary>
        /// 命令 RPOPLPUSH 在一个原子时间内,执行以下两个动作:
        /// 1. 将列表 source 中的最后一个元素( 尾元素)弹出,并返回给客户端。
        /// 2. 将 source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素。
        /// 举个例子,你有两个列表 source 和 destination , source 列表有元素 a, b, c , destination 列表有元素 x, y, z ,执行 RPOPLPUSH source destination 之后, source 列表包含元素 a, b , destination 列表包含元素 c, x, y, z ,并且元素 c 会被返回给客户端。
        /// 如果 source 不存在,值 nil 被返回,并且不执行其他动作。
        /// 如果 source 和 destination 相同,则列表中的表尾元素被移动到表头,并返回该元素,可以把这种特殊情况视作列表的旋转( rotation)操作。
        /// </summary>
        /// <param name="key1">第一个关键字</param>
        /// <param name="key2">第二个关键字</param>
        /// <returns>返回的移除的对象</returns>
        public OperateResult<string> ListRightPopLeftPush(string key1, string key2)
        {
            return OperateStringFromServer(new string[] { "RPOPLPUSH", key1, key2 });
        }

        /// <summary>
        /// 将一个或多个值 value 插入到列表 key 的表尾(最右边)。
        /// 如果 key 不存在,一个空列表会被创建并执行 RPUSH 操作。当 key 存在但不是列表类型时,返回一个错误。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">值</param>
        /// <returns>返回执行 RPUSH 操作后,表的长度。</returns>
        public OperateResult<int> ListRightPush(string key, string value)
        {
            return ListRightPush(key, new string[] { value });
        }

        /// <summary>
        /// 将一个或多个值 value 插入到列表 key 的表尾(最右边)。
        /// 如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表尾:比如对一个空列表 mylist 执行 RPUSH mylist a b c ,得出的结果列表为 a b c ,
        /// 如果 key 不存在,一个空列表会被创建并执行 RPUSH 操作。当 key 存在但不是列表类型时,返回一个错误。
        /// 返回执行 RPUSH 操作后,表的长度。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="values">值</param>
        /// <returns>返回执行 RPUSH 操作后,表的长度。</returns>
        public OperateResult<int> ListRightPush(string key, string[] values)
        {
            List<string> list = new List<string>();
            list.Add("RPUSH");
            list.Add(key);
            list.AddRange(values);

            return OperateNumberFromServer(list.ToArray());
        }

        /// <summary>
        /// 将值 value 插入到列表 key 的表尾,当且仅当 key 存在并且是一个列表。
        /// 和 RPUSH 命令相反,当 key 不存在时, RPUSHX 命令什么也不做。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="value">值</param>
        /// <returns>RPUSHX 命令执行之后,表的长度。</returns>
        public OperateResult<int> ListRightPushX(string key, string value)
        {
            return OperateNumberFromServer(new string[] { "RPUSHX", key, value });
        }

        #endregion

        #region Hash Operate

        /// <summary>
        /// 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="field">域</param>
        /// <returns>被成功移除的域的数量,不包括被忽略的域。</returns>
        public OperateResult<int> DeleteHashKey(string key, string field)
        {
            return DeleteHashKey(key, new string[] { field });
        }

        /// <summary>
        /// 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。返回被成功移除的域的数量,不包括被忽略的域。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="fields">所有的域</param>
        /// <returns>返回被成功移除的域的数量,不包括被忽略的域。</returns>
        public OperateResult<int> DeleteHashKey(string key, string[] fields)
        {
            List<string> list = new List<string>();
            list.Add("HDEL");
            list.Add(key);
            list.AddRange(fields);

            return OperateNumberFromServer(list.ToArray());
        }

        /// <summary>
        /// 查看哈希表 key 中,给定域 field 是否存在。如果哈希表含有给定域,返回 1 。
        /// 如果哈希表不含有给定域,或 key 不存在,返回 0 。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="field">域</param>
        /// <returns>如果哈希表含有给定域,返回 1 。如果哈希表不含有给定域,或 key 不存在,返回 0 。</returns>
        public OperateResult<int> ExistsHashKey(string key, string field)
        {
            return OperateNumberFromServer(new string[] { "HEXISTS", key, field });
        }

        /// <summary>
        /// 返回哈希表 key 中给定域 field 的值。当给定域不存在或是给定 key 不存在时,返回 nil 
        /// </summary>
        /// <param name="key">关键值</param>
        /// <param name="field">域</param>
        /// <returns>
        /// 给定域的值。
        /// 当给定域不存在或是给定 key 不存在时,返回 nil 。
        /// </returns>
        public OperateResult<string> ReadHashKey(string key, string field)
        {
            return OperateStringFromServer(new string[] { "HGET", key, field });
        }

        /// <summary>
        /// 返回哈希表 key 中,所有的域和值。在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。
        /// </summary>
        /// <param name="key">关键值</param>
        /// <returns>
        /// 以列表形式返回哈希表的域和域的值。
        /// 若 key 不存在,返回空列表。
        /// </returns>
        public OperateResult<string[]> ReadHashKeyAll(string key)
        {
            return OperateStringsFromServer(new string[] { "HGETALL", key });
        }

        /// <summary>
        /// 为哈希表 key 中的域 field 的值加上增量 increment 。增量也可以为负数,相当于对给定域进行减法操作。
        /// 如果 key 不存在,一个新的哈希表被创建并执行 HINCRBY 命令。返回执行 HINCRBY 命令之后,哈希表 key 中域 field 的值。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="field">域</param>
        /// <param name="value">增量值</param>
        /// <returns>返回执行 HINCRBY 命令之后,哈希表 key 中域 field 的值。</returns>
        public OperateResult<long> IncrementHashKey(string key, string field, long value)
        {
            return OperateLongNumberFromServer(new string[] { "HINCRBY", key, field, value.ToString() });
        }

        /// <summary>
        /// 为哈希表 key 中的域 field 的值加上增量 increment 。增量也可以为负数,相当于对给定域进行减法操作。
        /// 如果 key 不存在,一个新的哈希表被创建并执行 HINCRBY 命令。返回执行 HINCRBY 命令之后,哈希表 key 中域 field 的值。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="field">域</param>
        /// <param name="value">增量值</param>
        /// <returns>返回执行 HINCRBY 命令之后,哈希表 key 中域 field 的值。</returns>
        public OperateResult<string> IncrementHashKey(string key, string field, float value)
        {
            return OperateStringFromServer(new string[] { "HINCRBYFLOAT", key, field, value.ToString() });
        }

        /// <summary>
        /// 返回哈希表 key 中的所有域。当 key 不存在时,返回一个空表。
        /// </summary>
        /// <param name="key">关键值</param>
        /// <returns>
        /// 一个包含哈希表中所有域的表。
        /// 当 key 不存在时,返回一个空表。
        /// </returns>
        public OperateResult<string[]> ReadHashKeys(string key)
        {
            return OperateStringsFromServer(new string[] { "HKEYS", key });
        }

        /// <summary>
        /// 返回哈希表 key 中域的数量。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <returns>哈希表中域的数量。当 key 不存在时,返回 0 。</returns>
        public OperateResult<int> ReadHashKeyLength(string key)
        {
            return OperateNumberFromServer(new string[] { "HLEN", key });
        }

        /// <summary>
        /// 返回哈希表 key 中,一个或多个给定域的值。如果给定的域不存在于哈希表,那么返回一个 nil 值。
        /// 因为不存在的 key 被当作一个空哈希表来处理,所以对一个不存在的 key 进行 HMGET 操作将返回一个只带有 nil 值的表。
        /// </summary>
        /// <param name="key">关键值</param>
        /// <param name="fields">指定的域</param>
        /// <returns>
        /// 一个包含多个给定域的关联值的表,表值的排列顺序和给定域参数的请求顺序一样。
        /// </returns>
        public OperateResult<string[]> ReadHashKey(string key, string[] fields)
        {
            List<string> list = new List<string>();
            list.Add("HMGET");
            list.Add(key);
            list.AddRange(fields);

            return OperateStringsFromServer(list.ToArray());
        }

        /// <summary>
        /// 将哈希表 key 中的域 field 的值设为 value 。
        /// 如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。
        /// 如果域 field 已经存在于哈希表中,旧值将被覆盖。
        /// 如果 field 是哈希表中的一个新建域,并且值设置成功,返回 1 。
        /// 如果哈希表中域 field 已经存在且旧值已被新值覆盖,返回 0 。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="field">域</param>
        /// <param name="value">数据值</param>
        /// <returns>
        /// 如果 field 是哈希表中的一个新建域,并且值设置成功,返回 1 。
        /// 如果哈希表中域 field 已经存在且旧值已被新值覆盖,返回 0 。
        /// </returns>
        public OperateResult<int> WriteHashKey(string key, string field, string value)
        {
            return OperateNumberFromServer(new string[] { "HSET", key, field, value });
        }

        /// <summary>
        /// 同时将多个 field-value (域-值)对设置到哈希表 key 中。
        /// 此命令会覆盖哈希表中已存在的域。
        /// 如果 key 不存在,一个空哈希表被创建并执行 HMSET 操作。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="fields">域</param>
        /// <param name="values">数据值</param>
        /// <returns>
        /// 如果命令执行成功,返回 OK 。
        /// 当 key 不是哈希表(hash)类型时,返回一个错误
        /// </returns>
        public OperateResult WriteHashKey(string key, string[] fields, string[] values)
        {
            if (fields == null) throw new ArgumentNullException("fields");
            if (values == null) throw new ArgumentNullException("values");
            if (fields.Length != values.Length) throw new ArgumentException("Two arguement not same length");

            List<string> list = new List<string>();
            list.Add("HMSET");
            list.Add(key);
            for (int i = 0; i < fields.Length; i++)
            {
                list.Add(fields[i]);
                list.Add(values[i]);
            }

            return OperateStatusFromServer(list.ToArray());
        }

        /// <summary>
        /// 将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在。若域 field 已经存在,该操作无效。
        /// 设置成功,返回 1 。如果给定域已经存在且没有操作被执行,返回 0 。
        /// </summary>
        /// <param name="key">关键字</param>
        /// <param name="field">域</param>
        /// <param name="value">数据值</param>
        /// <returns>设置成功,返回 1 。如果给定域已经存在且没有操作被执行,返回 0 。</returns>
        public OperateResult<int> WriteHashKeyNx(string key, string field, string value)
        {
            return OperateNumberFromServer(new string[] { "HSETNX", key, field, value });
        }

        /// <summary>
        /// 返回哈希表 key 中所有域的值。当 key 不存在时,返回一个空表。
        /// </summary>
        /// <param name="key">关键值</param>
        /// <returns>
        /// 返回哈希表 key 中所有域的值。
        /// 当 key 不存在时,返回一个空表。
        /// </returns>
        public OperateResult<string[]> ReadHashValues(string key)
        {
            return OperateStringsFromServer(new string[] { "HVALS", key });
        }



        #endregion

        #region Server Operate

        /// <summary>
        /// SAVE 命令执行一个同步保存操作,将当前 Redis 实例的所有数据快照(snapshot)以 RDB 文件的形式保存到硬盘。
        /// </summary>
        /// <returns>保存成功时返回 OK 。</returns>
        public OperateResult Save()
        {
            return OperateStatusFromServer(new string[] { "SAVE" });
        }

        /// <summary>
        /// 在后台异步(Asynchronously)保存当前数据库的数据到磁盘。
        /// BGSAVE 命令执行之后立即返回 OK ,然后 Redis fork 出一个新子进程,原来的 Redis 进程(父进程)继续处理客户端请求,而子进程则负责将数据保存到磁盘,然后退出。
        /// </summary>
        /// <returns>反馈信息。</returns>
        public OperateResult SaveAsync()
        {
            return OperateStatusFromServer(new string[] { "BGSAVE" });
        }

        /// <summary>
        /// 获取服务器的时间戳信息,可用于本地时间的数据同步问题
        /// </summary>
        /// <returns>带有服务器时间的结果对象</returns>
        public OperateResult<DateTime> ReadServerTime()
        {
            OperateResult<string[]> times = OperateStringsFromServer(new string[] { "TIME" });
            if (!times.IsSuccess) return OperateResult.CreateFailedResult<DateTime>(times);

            long timeTick = long.Parse(times.Content[0]);
            DateTime dateTime = new DateTime(1970, 1, 1, 8, 0, 0).AddSeconds(timeTick);
            return OperateResult.CreateSuccessResult(dateTime);
        }

        #endregion

        #region Publish

        /// <summary>
        /// 将信息 message 发送到指定的频道 channel,返回接收到信息 message 的订阅者数量。
        /// </summary>
        /// <param name="channel">频道,和关键字不是一回事</param>
        /// <param name="message">消息</param>
        /// <returns>接收到信息 message 的订阅者数量。</returns>
        public OperateResult<int> Publish(string channel, string message)
        {
            return OperateNumberFromServer(new string[] { "PUBLISH", channel, message });

        }

        #endregion

        #region DB Block

        /// <summary>
        /// 切换到指定的数据库,数据库索引号 index 用数字值指定,以 0 作为起始索引值。默认使用 0 号数据库。
        /// </summary>
        /// <param name="db">索引值</param>
        /// <returns>是否切换成功</returns>
        public OperateResult SelectDB(int db)
        {
            return OperateStatusFromServer(new string[] { "SELECT", db.ToString() });
        }

        #endregion

        #region Private Member

        private string password = string.Empty;                 // 密码信息

        #endregion

        #region Object Override

        /// <summary>
        /// 返回表示当前对象的字符串
        /// </summary>
        /// <returns>字符串信息</returns>
        public override string ToString()
        {
            return $"RedisClient[{IpAddress}:{Port}]";
        }

        #endregion
    }
}