You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1110 lines
53 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using HslCommunication.Core;
using HslCommunication.Core.IMessage;
using HslCommunication.Core.Net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.IO;
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
}
}