ZeusAutomacao / DFe.NET

Biblioteca para Geração de NFe(2.0, 3.10 e 4.0) e NFCe(3.10 e 4.0) e consumo dos serviços necessários à sua manutenção, conforme descritos em http://www.nfe.fazenda.gov.br/portal/principal.aspx
GNU Lesser General Public License v2.1
754 stars 477 forks source link

A solicitação foi anulada: Não foi possível criar um canal seguro para SSL/TLS #1056

Closed sigridslima closed 4 years ago

sigridslima commented 5 years ago

Pessoal sei q já foram abertos vários tópicos porem nenhum foi conclusivo e não sanou meu problema. Então esse erro só acontece com os clientes que possuem o certificado A3, Quando o sistema fica ocioso ele apresenta isso, as vezes troco o protocolo TLS12 para TLS depois vice versa por varias tentativas ai funciona, mas na maioria das vezes tenho q reiniciar a aplicação, e outro problema também que percebi é quando altero a emissão de NF-e para emitir NFC-e e vice versa, também apresenta o erro, ai só a reinicialização do aplicativo. Ja o com certificado A1 não tenho esse problema emito notas simultaneamente inclusive via rede. Aguardo a colaboração de alguém.

marcosgerene commented 5 years ago

@sigridslima

Isso funciona para mim perfeitamente.

https://github.com/ZeusAutomacao/DFe.NET/issues/822#issuecomment-415055885

A questão foi completamente resolvida somente com a aplicação de console fazendo o processo, foi tentado tudo que está documentado na biblioteca.

O que foi tentado (que eu lembro):

Expect100Continue SecurityProtocol Deixar sem senha Using (para chamar o dispose) Manter em cache O problema também se repete no uso de outras bibliotecas (no meu caso com o ACBr.Net.CTe).

A solução encontrada foi o console application sendo fechado no final de cada requisição.

https://github.com/ZeusAutomacao/DFe.NET/issues/822#issuecomment-474361948

Esse erro é antigo aqui, eu cheguei a migrar meu CTe para o ACBr e acontecia a mesma coisa.

Talvez trabalhar com Thread e matar a segunda thread que faz o consumo da biblioteca seja a unica coisa que não tentei a fundo, mas sinceramente eu nem tentaria, usa a solução acima que é sucesso.

sigridslima commented 5 years ago

Então como poderia implementar o codigo do console do amigo?

static void Main(string[] args) { string metodo = string.Empty; string id = string.Empty; string empresa = string.Empty; string acesso = string.Empty; string justificativa = string.Empty;

try
{
    metodo = args[0];
    id = args[1];
    empresa = args[2];
    acesso = args[3];

    GArquivos.GravarLog($"ZeusApplication: {JsonConvert.SerializeObject(args)}");

    ConfigLocal.LerConfiguracoes();
    using (var conexao = new Conexao(_poolConnection: false))
    {
        new SistemaDAL(conexao).Load();
        new ConfiguracoesDAL(conexao).Load();
        Globals.DadosEmpresa = new EmpresaDAL(conexao).Load(int.Parse(empresa));
        AcessoBO.AcessoAtual = new AcessoDAL(conexao).Load(int.Parse(acesso));
    }

    switch (metodo.ToLower())
    {
        case "nfe_transmitir":
            bool forcarOffline = string.Equals(args[4].ToLower(), "true");
            new GDFe().Transmitir(long.Parse(id), forcarOffline);
            break;
        case "nfe_transmitir_lote":
            new GDFe().TransmitirLote(id);
            break;
        case "nfe_consultar": new GDFe().Consultar(long.Parse(id)); break;
        case "nfe_cancelar":
            justificativa = args[4];
            new GDFe().Cancelar(long.Parse(id), justificativa);
            break;
        case "nfe_cartacorrecao":
            string jsonCarta = GArquivos.CombinarDiretorio(Environment.CurrentDirectory, "temp", "carta_correcao.json");
            jsonCarta = GArquivos.LerArquivoTexto(jsonCarta);

            GArquivos.GravarLog(jsonCarta);
            new GDFe().CartaCorrecao(jsonCarta);
            break;
        case "cte_transmitir": new GCTe().Transmitir(long.Parse(id)); break;
        case "cte_transmitir_lote": new GCTe().TransmitirLote(id); break;
        case "cte_consultar": new GCTe().Consultar(long.Parse(id)); break;
        case "cte_cancelar":
            justificativa = args[4];
            new GCTe().Cancelar(long.Parse(id), justificativa);
            break;
        case "mdfe_transmitir": GMDFe.Transmitir(long.Parse(id)); break;
        case "mdfe_consultar": GMDFe.Consultar(long.Parse(id)); break;
        case "mdfe_encerrar": GMDFe.Encerrar(long.Parse(id)); break;
        case "mdfe_cancelar":
            justificativa = args[4];
            GMDFe.Cancelar(long.Parse(id), justificativa);
            break;
        default:
            throw new Exception("Método não suportado");
    }
}
catch (Exception ex)
{
    string PathZeus = GArquivos.CombinarDiretorio(AppDomain.CurrentDomain.BaseDirectory, "temp", "resposta.txt");

    string strLog = $"Zeus Console [Método: {metodo}] [Id: {id}] [Empresa:{empresa}] [Acesso:{acesso}]{Environment.NewLine}" +
                    $"Erro: {ex.Message}{Environment.NewLine}" +
                    $"Inner: {ex.InnerException}{Environment.NewLine}" +
                    $"StackTrace: {ex.StackTrace}";

    GArquivos.GravarLog(strLog);

    GArquivos.ExcluirArquivo(PathZeus);
    GArquivos.EscreverTexto(PathZeus, strLog);

}
finally
{
    GArquivos.GravarLog($"Finalizando ZeusApplication");
}

}

marcosgerene commented 5 years ago

@sigridslima

A implementação é bem tranquila, porque na teoria seu código já está pronto.

Vamos usar como exemplo o transmitir.

o seu método Transmitir(objetonfe) que você já tem na sua aplicação você colocaria no console e na aplicação você chamaria o console passando os atributos... neste comentário https://github.com/ZeusAutomacao/DFe.NET/issues/822#issuecomment-474361948 tem até o exemplo de chamada....

var process = new Process();
process.StartInfo.FileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "GereneSI.Zeus.Console.exe");
process.StartInfo.Arguments = $"NFE_TRANSMITIR {gnfe.Id} {Globals.DadosEmpresa.Codigo} {AcessoBO.AcessoAtual.Codigo} {(forcarOffline ? "true" : "false")}";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.Start();
process.WaitForExit();

Eu passo o método "NFE_TRANSMITIR" ou como você quiser chamar, passo o Id da nota no banco, porque depois eu carrego ele e passo algumas outas coisas que minha aplicação precisa como o Id da empresa logada, o código do acesso atual (para eu pegar o usuário atual e gerar logs).

Feito isso é só chamar o método...

static void Main(string[] args)
{
    string metodo = string.Empty;
    string id = string.Empty;
    string empresa = string.Empty;
    string acesso = string.Empty;
    string justificativa = string.Empty;

    try
    {
                //aqui são os argumentos, você coloca quantos precisar...
        metodo = args[0];
        id = args[1];
        empresa = args[2];
        acesso = args[3];

                //aqui gravo o log da chamada
        GArquivos.GravarLog($"ZeusApplication: {JsonConvert.SerializeObject(args)}");

        //regras de negócio do meu sistema ....
               ConfigLocal.LerConfiguracoes();

        using (var conexao = new Conexao(_poolConnection: false))
        {
            new SistemaDAL(conexao).Load();
            new ConfiguracoesDAL(conexao).Load();
            Globals.DadosEmpresa = new EmpresaDAL(conexao).Load(int.Parse(empresa));
            AcessoBO.AcessoAtual = new AcessoDAL(conexao).Load(int.Parse(acesso));
        }

        switch (metodo.ToLower())
        {
            case "nfe_transmitir":
                              //chama seu método de transmitir!
                new GDFe().Transmitir(long.Parse);
                break;
            case ... //outros métodos...
            default:
                throw new Exception("Método não suportado");
        }
    }
    catch (Exception ex)
    {
            //tratamento
    }
}

EU faço assim e para mim funciona.

sigridslima commented 5 years ago

Ola, infelizmente apresentou o mesmo erro, fiz somente uma rotina de conexao com o serviço, porem quando ele fica ocioso por três (3)minutos, exatamente 2:55 eu cronometrei, ele apresenta o erro, mas conseguir contornar com uma repetição a cada 1:30 ele se comunica com os servidores da Sefaz, sei que não e um pratica boa pois sempre ficará fazendo uma requisição. Mas ireir tentar outras praticas por aqui.

class Program { private const string ArquivoConfiguracao = @"\configuracao.xml"; static ConfiguracaoApp _configuracoes; static void Main(string[] args) { for (int i = 0; i <= 1000; i++) { //carregar configuração var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            _configuracoes = !File.Exists(path + ArquivoConfiguracao)
                ? new ConfiguracaoApp()
                : FuncoesXml.ArquivoXmlParaClasse<ConfiguracaoApp>(path + ArquivoConfiguracao);
            if (_configuracoes.CfgServico.TimeOut == 0)
                _configuracoes.CfgServico.TimeOut = 3000; //mínimo

            ///
            try
            {
                #region Status do serviço

                var servicoNFe = new ServicosNFe(_configuracoes.CfgServico);
                var retornoStatus = servicoNFe.NfeStatusServico();

                #endregion

                Console.WriteLine("Serviço Online");
                Console.ReadKey();

            }
            catch (Exception ex)
            {
                if (!string.IsNullOrEmpty(ex.Message))
                    //MessageBox.Show(ex.Message, "Erro Geral");

                Console.WriteLine("Sem Conexao");
                Console.ReadKey();
                //ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
                //ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            }
        }                
    }
}
marcosgerene commented 5 years ago

@sigridslima

Eu sinceramente não entendi o seu cenário...

Da forma como te passe a aplicação "morre" depois de cada chamada, não existe a possibilidade de acontecer erros por inatividade.

sigridslima commented 5 years ago

Então fiz como foi citado acima mas apresentou o mesmo erro depois de um tempo ocioso.

1º Criei o console com minha chamada de verificação de serviço.

region Status do serviço

            var servicoNFe = new ServicosNFe(_configuracoes.CfgServico);
            var retornoStatus = servicoNFe.NfeStatusServico();

            #endregion

            Console.WriteLine("Serviço Online");
            Console.ReadKey();

2º No aplicativo tenho a chamada do console

var process = new Process(); process.StartInfo.FileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ConsoleA3.exe"); // process.StartInfo.Arguments = $"NFE_TRANSMITIR {gnfe.Id} {Globals.DadosEmpresa.Codigo} {AcessoBO.AcessoAtual.Codigo} {(forcarOffline ? "true" : "false")}"; process.StartInfo.CreateNoWindow = true; process.StartInfo.UseShellExecute = false; process.Start(); process.WaitForExit();

Peço por gentileza por favor que analise onde errei. Obrigado

marcosgerene commented 5 years ago

@sigridslima

Não consigo analisar seu código assim, mas a teoria está correta.

Entretanto, o problema da "inatividade" não existiria porque o console "morre" sempre, cada requisição seria sempre uma requisição "nova" em uma aplicação que acabou de nascer.

danilobreda commented 5 years ago

@sigridslima conseguiu resolver ou ainda esta com o problema?

danilobreda commented 5 years ago

Como pode ver nessa pesquisa do google Esse erro é algo recorrente por outros usuários de outros sistemas e frameworks. Acredito que por isso seja uma falha a nível abaixo do framework Zeus. Uma das dicas que pesquisei foi a utilização do tls 1.2 e o uso de um FOR de tentativa de pelo menos 5 vezes...

danilobreda commented 5 years ago

@sigridslima nas configurações do NFE tenta colocar o seguinte codigo:

                    ValidarCertificadoDoServidor = false,
sigridslima commented 5 years ago

Ainda não conseguir solucionar, fiz um paliativo de 2 em 2 minutos faz uma requisição para ver se esta online o serviço, ai nao apresentou o problema nao, mas quando fica offline e passa dos 3 minutos apresenta o erro ai tenho que reiniciar a aplicação, Vou tentar fazer um for para ver. obrigado

stale[bot] commented 4 years ago

Essa Issue foi marcada automáticamente como obsoleta, devido a um longo periodo de inatividade. Se nenhuma interação ocorrer nos próximos dias, ela será encerrada. Agradecemos a sua contribuição, esse processo é apenas para manter o repositório mais organizado.