using HslCommunication.Core.Net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace HslCommunication.Enthernet.Redis
{
///
/// Redis协议的订阅操作,一个对象订阅一个或是多个频道的信息
///
public class RedisSubscribe : NetworkXBase
{
#region Constructor
///
/// 实例化一个发布订阅类的客户端,需要指定ip地址,端口,及订阅关键字
///
/// 服务器的IP地址
/// 服务器的端口号
/// 订阅关键字
public RedisSubscribe( string ipAddress, int port, string[] keys )
{
endPoint = new IPEndPoint( IPAddress.Parse( ipAddress ), port );
keyWords = keys;
if (keys == null)
{
throw new Exception( StringResources.Language.KeyIsNotAllowedNull );
}
}
///
/// 实例化一个发布订阅类的客户端,需要指定ip地址,端口,及订阅关键字
///
/// 服务器的IP地址
/// 服务器的端口号
/// 订阅关键字
public RedisSubscribe( string ipAddress, int port, string key )
{
endPoint = new IPEndPoint( IPAddress.Parse( ipAddress ), port );
keyWords = new string[] { key };
if (string.IsNullOrEmpty( key ))
{
throw new Exception( StringResources.Language.KeyIsNotAllowedNull );
}
}
#endregion
#region Private Method
private OperateResult CreatePush( )
{
CoreSocket?.Close( );
OperateResult connect = CreateSocketAndConnect( endPoint, 5000 );
if (!connect.IsSuccess) return connect;
// 密码的验证
if (!string.IsNullOrEmpty( this.Password ))
{
OperateResult check = Send( connect.Content, RedisHelper.PackStringCommand( new string[] { "AUTH", this.Password } ) );
if (!check.IsSuccess) return check;
OperateResult checkResult = RedisHelper.ReceiveCommand( connect.Content );
if (!checkResult.IsSuccess) return checkResult;
string msg = Encoding.UTF8.GetString( checkResult.Content );
if (!msg.StartsWith( "+OK" )) return new OperateResult( msg );
}
List lists = new List( );
lists.Add( "SUBSCRIBE" );
lists.AddRange( keyWords );
OperateResult send = Send( connect.Content, RedisHelper.PackStringCommand( lists.ToArray( ) ) );
if (!send.IsSuccess) return send;
CoreSocket = connect.Content;
try
{
connect.Content.BeginReceive( new byte[0], 0, 0, SocketFlags.None, new AsyncCallback( ReceiveCallBack ), connect.Content );
}
catch(Exception ex)
{
return new OperateResult( ex.Message );
}
return OperateResult.CreateSuccessResult( );
}
private void ReceiveCallBack( IAsyncResult ar )
{
if(ar.AsyncState is Socket socket)
{
try
{
int receive = socket.EndReceive( ar );
OperateResult read = RedisHelper.ReceiveCommand( socket );
if (!read.IsSuccess)
{
SocketReceiveException( null );
return;
}
else
{
socket.BeginReceive( new byte[0], 0, 0, SocketFlags.None, new AsyncCallback( ReceiveCallBack ), socket );
}
OperateResult data = RedisHelper.GetStringsFromCommandLine( read.Content );
if (!data.IsSuccess) {
LogNet?.WriteWarn( data.Message );
return;
}
if(data.Content[0].ToUpper() == "SUBSCRIBE")
{
return;
}
else if(data.Content[0].ToUpper( ) == "MESSAGE")
{
action?.Invoke( data.Content[1], data.Content[2] );
}
else
{
LogNet?.WriteWarn( data.Content[0] );
}
}
catch (ObjectDisposedException)
{
// 通常是主动退出
return;
}
catch(Exception ex)
{
SocketReceiveException( ex );
}
}
}
private void SocketReceiveException( Exception ex )
{
// 发生异常的时候需要进行重新连接
while (true)
{
if (ex != null) LogNet?.WriteException( "Offline", ex );
Console.WriteLine( StringResources.Language.ReConnectServerAfterTenSeconds );
System.Threading.Thread.Sleep( this.reconnectTime );
if (CreatePush( ).IsSuccess)
{
Console.WriteLine( StringResources.Language.ReConnectServerSuccess );
break;
}
}
}
#endregion
#region Public Properties
///
/// 如果Redis服务器设置了密码,此处就需要进行设置。必须在CreatePush方法调用前设置
///
public string Password { get; set; }
#endregion
#region Public Method
///
/// 创建数据推送服务
///
/// 触发数据推送的委托
/// 是否创建成功
public OperateResult CreatePush( Action pushCallBack )
{
action = pushCallBack;
return CreatePush( );
}
///
/// 关闭消息推送的界面
///
public void ClosePush( )
{
action = null;
CoreSocket?.Close( );
}
#endregion
#region Private Member
private IPEndPoint endPoint; // 服务器的地址及端口信息
private string[] keyWords = null; // 缓存的订阅关键字
private Action action; // 服务器推送后的回调方法
private int reconnectTime = 10000; // 重连服务器的时间
#endregion
#region Object Override
///
/// 返回表示当前对象的字符串
///
/// 字符串信息
public override string ToString( )
{
return $"RedisSubscribe[{endPoint}]";
}
#endregion
}
}