Solucionado (ver solução)
Solucionado
(ver solução)
5
respostas

Aula 1 : O comando resposta.OutputStream.Write não está funcionando em tempo de execução

Olá,

Estou tentando reproduzir os resultados da primeira aula, porém quando digito no browser o endereço do CSS, na maioria das vezes não retorna resultado algum (no chrome retorna o erro ERR_CONNECTION_RESET).

Quando rodo a aplicação no modo de depuração e coloco um breakpoint antes do comando resposta.OutputStream.Write a aplicação retorna texto esperado para o browser. Alguém sabe o que é este e como resolve-lo? Obrigado.

    public class WebApplication
    {
        private readonly string[] _prefixos;

        public WebApplication(string[] prefixos)
        {
            if (prefixos == null)
                throw new ArgumentNullException(nameof(prefixos));

            _prefixos = prefixos;
        }

        public void iniciar()
        {
            while(true)
                ManipulaRequests();

        }

        private void ManipulaRequests()
        {
            var httpListener = new HttpListener();

            foreach (string prefixo in _prefixos)
            {
                httpListener.Prefixes.Add(prefixo);
            }
            httpListener.Start();
            HttpListenerContext  contexto = httpListener.GetContext();
            HttpListenerRequest  requisicao = contexto.Request;
            HttpListenerResponse resposta = contexto.Response;

            var path = requisicao.Url.AbsolutePath;

            if (path== "/Assets/css/styles.css")
            {
                //retorna o arquivo css
                var assembly = Assembly.GetExecutingAssembly();
                var resourceStream = assembly.GetManifestResourceStream("ByteBank_Portal.Assets.css.styles.css");
                var bytesResource = new byte[resourceStream.Length];
                resourceStream.Read(bytesResource, 0, (int)resourceStream.Length);


                resposta.ContentType = "text/css;charset=utf-8";
                resposta.StatusCode = 200;
                resposta.ContentLength64 = resourceStream.Length;


                try
                {
                    resposta.OutputStream.Write(bytesResource, 0, bytesResource.Length);
                    Console.WriteLine("passou");
                }
                catch (Exception exp)
                {
                    Console.WriteLine(""+exp.Source+" - " + exp.Message);
                }
                resposta.OutputStream.Close();
            }

         //...       

             httpListener.Stop();
        }
    }
5 respostas

O código passou a funcionar depois que coloquei uma espera (Thread.Sleep(100)) depois do resposta.OutputStream.Write, possivelmente a instrução de close esta sendo realizada antes da instrução write acabar. Porém alguém sabe se existe uma solução melhor, que não precise chutar um tempo para espera?

Olá, Gabriel.

Tenho uma sugestão: tente dar um Flush() antes do Close():

                resposta.OutputStream.Flush();
                resposta.OutputStream.Close();
solução!

Olá, Gabriel. Tudo bom?

Apesar da API do .NET tornar bem simples para nós, conexões de rede dependem de muitos recursos relacionados ao sistema operacional e sua máquina em si, além da forma que o navegador cria e mantém conexões - consegui reproduzir na minha máquina ao estressar a aplicação com várias chamadas simultâneas.

O código desenvolvido no curso não leva em consideração, por exemplo, várias chamadas simultâneas e isso é algo que certamente o Chrome está fazendo - no mínimo, existe a requisição para o documento .css e para o ícone do site favicon.ico.

Podemos tentar uma alternativa um pouco mais complexa, que é fazendo o uso de um método CallBack para tratar as requisições quando obtiverem o Contexto de forma assíncrona! Segue o código da classe WebApplication com estas mudanças:

public class WebApplication
{
    private readonly string[] _prefixos;

    public WebApplication(string[] prefixos)
    {
        if (prefixos == null)
            throw new ArgumentNullException(nameof(prefixos));
        _prefixos = prefixos;
    }

    private HttpListener httpListener;
    public void Iniciar()
    {
        httpListener = new HttpListener();

        foreach (var prefixo in _prefixos)
            httpListener.Prefixes.Add(prefixo);

        httpListener.Start();

        // Deste modo, trataremos as requisições de forma assíncrona
        // ou seja, a linha a seguir é não-bloqueante e  o  primeiro 
        // parâmetro é o callback a ser invocado quando  o  contexto
        // for obtido.
        httpListener.BeginGetContext(CallBack_QuandoContextoForObtido, null);

        // Como a linha anterior é assíncrona e não bloqueante, uso
        // esta linha para o processo da aplicação não encerrar.
        Console.ReadLine();
    }

    private void CallBack_QuandoContextoForObtido(IAsyncResult asyncResult)
    {
        var contexto = httpListener.EndGetContext(asyncResult);

        // Antes  de  tratar  a  requisição   atual,    já
        // deixaremos o httpListener aguardando a próxima,
        // chamando BeginGetContext novamente.
        httpListener.BeginGetContext(CallBack_QuandoContextoForObtido, null);

        // Daqui para baixo é exatamente o mesmo  código
        // com exceção da chamada do método Stop(), pois
        // manteremos a nossa instância httpListener  já
        // preparada para a próxima requisição!
        var requisicao = contexto.Request;
        var resposta = contexto.Response;

        var path = requisicao.Url.AbsolutePath;

        if (path == "/Assets/css/styles.css")
        {
            // Retornar o nosso documento styles.css
            var assembly = Assembly.GetExecutingAssembly();

            var nomeResource = "ByteBank.Portal.Assets.css.styles.css";
            var resourceStream = assembly.GetManifestResourceStream(nomeResource);
            var bytesResource = new byte[resourceStream.Length];

            resourceStream.Read(bytesResource, 0, (int)resourceStream.Length);

            resposta.ContentType = "text/css; charset=utf-8";
            resposta.StatusCode = 200;
            resposta.ContentLength64 = resourceStream.Length;

            resposta.OutputStream.Write(bytesResource, 0, bytesResource.Length);

            resposta.OutputStream.Close();
        }
        else if (path == "/Assets/js/main.js")
        {
            // Retornar o nosso documento main.js
            var assembly = Assembly.GetExecutingAssembly();

            var nomeResource = "ByteBank.Portal.Assets.js.main.js";
            var resourceStream = assembly.GetManifestResourceStream(nomeResource);
            var bytesResource = new byte[resourceStream.Length];

            resourceStream.Read(bytesResource, 0, (int)resourceStream.Length);

            resposta.ContentType = "application/js; charset=utf-8";
            resposta.StatusCode = 200;
            resposta.ContentLength64 = resourceStream.Length;

            resposta.OutputStream.Write(bytesResource, 0, bytesResource.Length);

            resposta.OutputStream.Close();
        }

        // Não daremos Stop, pois o HttpListener continuará
        // pronto para outra requisição!
        // httpListener.Stop();
    }
}

Em meus testes, o erro deixou de acontecer. Você poderia testar aí?

--

Alexandre Aquiles, boa! Mas, tenho receio que esta abordagem nos traga a falsa impressão de problema solucionado.

O método Stream::Close(), internamente, invoca o método Flush() e podemos ter a impressão de que esta foi a solução, porque, uma invocação acaba atuando como um processamento e causando um intervalo maior até a chamada de httpListener.Stop();, como o paliativo Thread.Sleep(100) usado pelo Gabriel.

Perfeito, Guilherme!

Olá Guilherme,

Obrigado pela explicação, o exemplo que você deu funcionou perfeitamente.

Valeu pela força também, Alexandre.