Ir para o conteúdo

 Voltar a Linguagem Lua
Tela cheia

Chamada a tcp.execute nunca retorna

28 de Janeiro de 2010, 15:51 , por Desconhecido - | Ninguém seguindo este artigo por enquanto.
Visualizado 12 vezes

Pessoal, já utilizei a classe tcp disponibilizada pelo Francisco algumas vezes sem problemas. Porém, gostaria de criar uma função para enviar uma requisição GET a uma página e obter o HTML retornado. 

Sem criar tal função, o código para tal fim ficaria como:

    local content = ""
    tcp.execute(
        function ()
            tcp.connect(host, 80)
            local url = "GET "..uri.." HTTP/1.0\n"
            tcp.send(url)
            content = tcp.receive("*a")
            if content then
               createFile(content, fileName)
            end
            tcp.disconnect()
        end
    )   

Veja que dentro do if, se foi retornando algum conteúdo, o mesmo é salvo em um arquivo.

Porém, gostaria de criar uma função como:

function getWebPage(host, uri)
    local content = nil
    tcp.execute(
        function ()
            tcp.connect(host, 80)
            local url = "GET "..uri.." HTTP/1.0\n"
            tcp.send(url)
            content = tcp.receive("*a")
            tcp.disconnect()
        end
    )    
    return content --por algum motivo, nunca chega aqui
end

Desta forma, bastaria chamá-la como:

local content = getWebPage(host, uri) --a função nunca retorna
--O if/else abaixo nunca é executado, pois getWebPage
--nunca retorna
if content then
   createFile(content, fileName)
else
   print("Nenhum conteúdo foi retornado")
end

Porém, desta forma, a função getWebPage nunca retorna, não chegando nunca na linha do if.

A vantagem de utilizar uma função como a getWebPage é que esconde toda a complexidade das chamadas da classe TCP.

Analisei todo o código do arquivo tcp.lua mas não consegui entender o motivo disso. Compreendo como funcionam as funções de co-rotinas, mas não encontrei o motivo de tcp.execute nunca retornar para quem o chamou.

Autor: Manoel Campos da Silva Filho


44 comentários

  • 1e29d0095154ec9fed26313e9afb8f36?only path=false&size=50&d=404Manoel Campos da Silva Filho(usuário não autenticado)
    31 de Janeiro de 2010, 22:38

     

    Pessoal, descobri qual o problema. Na verdade, na função getWebPage, a chamada a tcp.execute retorna imediatamente, já executando a linha return content. Porém, esta variável content só é atribuída dentro da função anônima passada à tcp.execute, que cria uma co-rotina para executar esta função.

    Assim, seria preciso que getWebPage ficasse aguardando até a função anônima, passada a tcp.execute, retornasse, para só então executar o return content.

    Já li o manual do Lua a respeito de co-rotinas, li o PIL 2 também, mas não consegui entender como fazer para incluir esse comportamento.

    Tentei algumas soluções, como a mostrada abaixo, com seus devidos comentários, mas não resolveu.

    Agradeço qualquer ajuda. Segue código testado sem sucesso:

    function getWebPage(host, uri)
     local content = nil
     local co = false
     --body function da co-rotina co
     function f()
        tcp.execute(
            function ()
                tcp.connect(host, 80)
                local url = "GET "..uri.." HTTP/1.0\n"
                tcp.send(url)
                --somente aqui que o retorno da função getWebPage é obtido
                content = tcp.receive("*a")
                tcp.disconnect()
                --só continua a execução da co-rotina depois
                --que a conexão for fechada (na linha acima)
                coroutine.resume(co)
            end
        )   
        --após a execução de tcp.execute (que retorna imediatamente),
        --suspende a execução da co-rotina
        coroutine.yield()
     end

     --cria uma co-rotina para executar todo o
     --da body function f
     co = coroutine.create(f)

     --inicia a co-rotinas, mas pelo que vi, o resume retorna imediatamente,
     --sendo que seria necessário que aguardasse até a conexão tcp ser fechada
     coroutine.resume(co)

     --retorna o conteúdo obtido dentro da função anônima
     --passada a tcp.execute
     return content
    end

    • 1e29d0095154ec9fed26313e9afb8f36?only path=false&size=50&d=404Manoel Campos da Silva Filho(usuário não autenticado)
      3 de Fevereiro de 2010, 11:24

       

      Pessoal, descobri qual o problema mas não encontrei uma solução ideal ainda. Quando tcp.execute é chamada, ela cria uma co-rotina para execução da função anônima passada a ela, porém, a primeira retorna imediatamente, não aguardando  a segunda terminar. Assim, getWebPage retornará nil imediatamente, pois a resposta da requisição TCP ainda não foi obtido.

      Para resolver isso, a única forma que encontrei, foi chamar a função getWebPage (e qualquer outra que use tcp.execute), dentro de uma co-rotina. Com isto, eu posso suspender essa co-rotina até que o retorno da requisição seja obtido. Porém, mesmo com a criação da co-rotina, não consigo fazer com que ela retorne o resultado da requisição HTTP, pois a criação da co-rotina e início de execução (resume) também retorna imediatamente. Assim, a única forma de resolver foi, passar uma função como parâmetro para a co-rotina, para ser executada como callback.

      Para tentar esclarecer melhor, segue trecho de código, comentado, utilizando algumas funções que criei, para encapsular toda as chamadas de uma requisição TCP.

      Se alguém souber outra forma de fazer isso, sem uso de uma co-rotina adicional e sem uso de callbacks, agradeço.

      Se desejarem obter o módulo HTTP que criei, está disponível neste link. O mesmo permite envio de requisições HTTP e até download de arquivos por tal protocolo.

       

      local fileName = "image.jpg"

      ---Envia uma requisição HTTP para baixar uma imagem de um servidor web
      --@param imageUrl URL da imagem a ser baixada
      --@param _callback Função de callback a ser
      --executada quando a imagem for obtida do servidor web.
      local function getImage(imageUrl, _callback)
        if http.getFile(imageUrl, fileName) then
          --Após baixar a imagem, chama a função de callback p/ exibir a mesma
          _callback()
        end
      end

      --Função de callback a ser
      --executada quando a imagem for obtida do servidor,
      --para exibir a mesma na tela.
      local function showImageCallback()
          local img = canvas:new(fileName)
          canvas:compose(1, 1, img)
          canvas:flush()
      end

      --Cria uma co-rotina para executar a função getImage, definida acima.
      --O parâmetro showImageCallback é uma função de callback
      --que será chamada quando getImage retornar, tendo concluído o download da imagem.
      util.coroutineCreate(getImage, showImageCallback)

       

      • 820d3515c25dc6c1319875ee4fed9224?only path=false&size=50&d=404Carlos de Salles Soares Neto(usuário não autenticado)
        3 de Fevereiro de 2010, 11:46

         

        Manoel,

        Sua solucao me pareceu limpa. Qual o problema que vc v^e nela? Achou confusa, dificil de entender?

        Carlos

        • 1e29d0095154ec9fed26313e9afb8f36?only path=false&size=50&d=404Manoel Campos da Silva Filho(usuário não autenticado)
          3 de Fevereiro de 2010, 11:55

           

          Acredito que para quem não conhece como funcionam as co-rotinas de lua, isso pode ser meio estranho. Afinal, se existe uma função, espera-se que a mesma só retorne depois de ter o resultado a ser devolvido. Assim, um usuário não familizarizado com co-rotinas não entenderá porque chama a função getWebPage diretamente e a mesma só retorna nil.

          No módulo HTTP que disponibilizei no link anterior, explico como tudo funciona e dou um exemplo.

          Se tiver como fazer direto, sem co-rotina adicional, seria o ideal.

Concurso ITU-T de Aplicações para IPTV 2012

13 de Agosto de 2012, 19:38, por Desconhecido

Gostaríamos de lembrar aos possíveis interessados que o prazo de registro para participação no Concurso ITU-T de Aplicações para IPTV 2012 (IPTV Application Challenge) se encerra nesta semana, dia 15 de agosto de 2012. Já o prazo para a submissão de aplicações se encerra no dia 07 de setembro de 2012.



NCL Eclipse 1.6 disponível

10 de Janeiro de 2012, 21:19, por Desconhecido

Caros membros da Comunidade Ginga,



Concursos de Aplicações Ginga-NCL

22 de Setembro de 2011, 3:22, por Desconhecido

    Gostaríamos de relembra-los de que há dois concursos de aplicações Ginga-NCL com inscrições ainda abertas. O convite é aberto a toda a comunidade de desenvolvedores de aplicações para o Middleware Ginga-NCL, em nível internacional. São os seguintes concursos:



Novas versões: Ginga e Ginga-NCL Virtual Set-top Box (v.0.12.3)

1 de Agosto de 2011, 20:58, por Desconhecido



Algumas Boas Notícias da Comunidade Ginga

28 de Julho de 2011, 21:31, por Desconhecido

Autor: Roberto Azevedo