Android и NodeMCU, получение ответа от сервера не работает должным образом?

Я написал приложение на Android, которое реализует отправку простых запросов (используя Volley) на сервер. Сервер стоит на микроконтроллере NodeMCU (ESP8266), написанном на Lua. Проблема в том, что после отправки запроса приложение не всегда может напечатать ответ. Если адрес, например. "http://www.google.com" правильно отправляет запрос, получает и отображает ответ, но если это адрес из кода ниже - он правильно отправляет запрос (сервер реагирует), но не (?) получает ответ (не отображает его, выводит: "Это не сработало!"). Есть ли у вас какие-либо идеи, как я могу это исправить и иметь возможность распечатать ответ?

Android (часть, отвечающая за отправку запросов):

buttonSynchro.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {


        // Instantiate the RequestQueue.
        String url = "http://192.168.1.12/";


        // Request a string response from the provided URL.
        StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        // Display the first 500 characters of the response string.
                        testTextView.setText("Response is: "+ response.substring(0,500));
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                testTextView.setText("That didn't work!");
            }
        });


        // Add the request to the RequestQueue.
        RequestQueue queue = Volley.newRequestQueue(SettingsActivity.this);
        queue.add(stringRequest);
    }
});  

NodeMCU, Lua:

station_cfg={}
station_cfg.ssid="Dom"
station_cfg.pwd="lalala"
wifi.sta.config(station_cfg)
function receive(conn, request)
        print(request)
        print()
        local buf = "";
        buf = buf.."<!doctype html><html>";
        buf = buf.."<h1> ESP8266 Web Server</h1>";
        buf = buf.."</html>";

        conn:send(buf);
        conn:on("sent", function(sck) sck:close() end);     
        collectgarbage();

end

function connection(conn) 
    conn:on("receive", receive) 

end

srv=net.createServer(net.TCP, 30) 
srv:listen(80, connection)

person Czarek    schedule 09.03.2018    source источник
comment
проверьте, работает ли в вашем браузере адрес 192.168.1.12. :-)   -  person user6327816    schedule 10.03.2018
comment
Да, конечно, это работает. Я думал, что уже говорил это :)   -  person Czarek    schedule 10.03.2018
comment
Кстати, вы не сказали, что это работает в браузере, вы сказали, что сервер отреагировал, я предполагаю, что это означает, что вы видели запрос, напечатанный на esp.   -  person nPn    schedule 10.03.2018
comment
@nPn Да, я не сказал этого прямо. Итак, чтобы было понятно: после отправки запроса я вижу запрос, напечатанный на esp, а после отправки запроса через браузер я вижу страницу, созданную с HTML-кодом, показанным выше.   -  person Czarek    schedule 10.03.2018


Ответы (3)


Код nPn работает в некоторых пользовательских агентах (Chrome/Firfox/curl/wget в macOS), но не в других (Safari в macOS и iOS, Firefox Klar в iOS). Вероятно, это связано с отсутствием заголовков HTTP.

Я советую вам придерживаться примера, который есть в нашей документации по адресу https://nodemcu.readthedocs.io/en/latest/en/modules/net/#netsocketsend.

srv = net.createServer(net.TCP)

function receiver(sck, data)
  print(data)
  print()

  -- if you're sending back HTML over HTTP you'll want something like this instead
  local response = {"HTTP/1.0 200 OK\r\nServer: NodeMCU on ESP8266\r\nContent-Type: text/html\r\n\r\n"}

  response[#response + 1] = "<!doctype html><html>"
  response[#response + 1] = "<h1> ESP8266 Web Server</h1>"
  response[#response + 1] = "</html>"

  -- sends and removes the first element from the 'response' table
  local function send(localSocket)
    if #response > 0 then
      localSocket:send(table.remove(response, 1))
    else
      localSocket:close()
      response = nil
    end
  end

  -- triggers the send() function again once the first chunk of data was sent
  sck:on("sent", send)

  send(sck)
end

srv:listen(80, function(conn)
  conn:on("receive", receiver)
end)

Кроме того, ваш код (и nPn в этом отношении) делает предположения о доступности Wi-Fi там, где этого не должно быть.

wifi.sta.config(station_cfg) (с auto-connect=true) и wifi.stat.connect являются асинхронными и, следовательно, неблокирующими, как и многие другие API-интерфейсы NodeMCU. Следовательно, вы должны поместить приведенный выше код в функцию и вызывать ее только после того, как устройство подключится к точке доступа и получит IP-адрес. Вы делаете это, например. регистрация обратного вызова для события STA_GOT_IP с помощью монитора событий WiFi. Вы найдете очень сложный пример последовательности загрузки, которая прослушивает все события WiFi, по адресу https://nodemcu.readthedocs.io/en/latest/en/upload/#initlua. Для начала вы можете обрезать это и слушать только полученный IP.

person Marcel Stör    schedule 10.03.2018
comment
Я хотел бы добавить, что это не работает (для меня) с методом отправки запросов Volley. Но он работает с методом отправки запросов, доступным здесь получить содержимое"> stackoverflow.com/questions/44377993/ Код Lua из моего вопроса здесь не работает для обоих методов. Спасибо. - person Czarek; 11.03.2018
comment
Какая? Я в замешательстве... Вы приняли ответ, хотя он вам не подходит? Код Lua действительно работает, это гарантировано. Я тестировал все семь пользовательских агентов, упомянутых в ответе. - person Marcel Stör; 11.03.2018
comment
Мы не поняли друг друга. Ваш код Lua работает. Метод отправки запроса Volley не работает (для меня) (я не могу распечатать ответ). Метод отправки запросов, упомянутый в ссылке, которую я разместил в своем последнем комментарии, правильно работает с вашим кодом Lua. В моем последнем комментарии я не имел в виду, что ваш код Lua не работает. Может быть, я должен начать: метод отправки запросов Volley не работает (для меня) с этими. - person Czarek; 11.03.2018
comment
Также может быть полезно распечатать ошибку, которую возвращает Volley. - person nPn; 11.03.2018
comment
Я не умею вставлять сюда обычный текст, поэтому дам ссылку. Приложение отправляет запрос (сервер реагирует), но потом перестает работать. ibb.co/fUoRCn - person Czarek; 11.03.2018
comment
Ваша проблема заключается в том, что IndexOutOfBoundsException отображается в вашей трассировке Java. Вы запрашиваете подстроку от 0 до 500, но ответ составляет всего 483 символа. Я опубликую простой ответ. - person nPn; 14.03.2018

На основании вашего комментария выше и опубликованной вами ссылки, показывающей трассировку, ваше приложение для Android аварийно завершает работу в onResponse() потому что вы запрашиваете подстроку длиннее фактической длины строки.

Вы можете исправить это несколькими способами, но один из них состоит в том, чтобы сделать конечный индекс минимальной длиной ответа и 500 (я предполагаю, что это максимум, который вы можете использовать в своем TextView?). Вы можете попробовать изменить

testTextView.setText("Response is: "+ response.substring(0,500));

to

testTextView.setText("Response is: "+ response.substring(0, Math.min(response.length(), n)));

или любой другой способ, который вы считаете более подходящим, чтобы ограничить длину ответа, который не вызывает IndexOutOfBoundsException

См. метод подстроки здесь

подстрока общедоступной строки (int beginIndex, int endIndex)

Возвращает новую строку, которая является подстрокой этой строки. Подстрока начинается с указанного beginIndex и продолжается до символа с индексом endIndex - 1. Таким образом, длина подстроки равна endIndex-beginIndex.

Примеры:

 "hamburger".substring(4, 8) returns "urge"
 "smiles".substring(1, 5) returns "mile"

Параметры: beginIndex - начальный индекс включительно. endIndex - конечный индекс, исключительный. Возвращает: указанную подстроку. Выдает: IndexOutOfBoundsException — если beginIndex имеет отрицательное значение, или endIndex больше, чем длина этого объекта String, или beginIndex больше, чем endIndex.

person nPn    schedule 13.03.2018
comment
Да, это решило проблему с залпом. Я взял этот пример 1:1 из документации на developer.android.com и не думал, что это может не работать в этом, простом случае. - person Czarek; 14.03.2018

Я не эксперт по Lua, но я думаю, что вы регистрируете свой «отправленный» обратный вызов после отправки ответа.

Я думаю, вы должны переместить его в функцию подключения:

station_cfg={}
station_cfg.ssid="Dom"
station_cfg.pwd="lalala"
wifi.sta.config(station_cfg)
function receive(conn, request)
        print(request)
        print()
        local buf = "";
        buf = buf.."<!doctype html><html>";
        buf = buf.."<h1> ESP8266 Web Server</h1>";
        buf = buf.."</html>";
        conn:send(buf);  
        collectgarbage();

end

function connection(conn) 
    conn:on("receive", receive) 
    conn:on("sent", function(sck) sck:close() end);   
end

srv=net.createServer(net.TCP, 30) 
srv:listen(80, connection)
person nPn    schedule 10.03.2018
comment
Спасибо за готовность помочь, но это не решило проблему. Теперь я вижу запрос, напечатанный на esp (сервер реагирует), но я не могу загрузить страницу с помощью браузера. - person Czarek; 10.03.2018
comment
И, конечно же, я не могу распечатать ответ в приложении для Android. - person Czarek; 10.03.2018