I currently try to write a android app to setup and controll a ESP8266 on which micropython runs.
On the micropython server I initialize a websocket like this:
def __init__(self, task_manager, setup_mode):
address = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
self._socket = socket.socket()
self._socket.bind(address)
self._socket.listen(1)
self._socket.setblocking(False)
self._socket.settimeout(5)
self._task_manager = task_manager
self._setup_mode = setup_mode
print('New Socket is listening on: ', address)
And then simple listen to incoming connections like this, and then react to the incoming messages. Also the listing is looped to allow the microcontroller logic to update every 5 seconds.
client, address = self._socket.accept()
print("New request from:", address)
Everything is working fine when I send test request using python from my PC. For example a simple request would be something like this:
data = json.dumps({'load': {'type': "is_lighthub", 'data': {}}})
response = requests.post(ip, json=data)
However when I try to make the same post request using OkHttp from an android app, then there is no incoming connection at the ESP.
Here is the android java code:
private void addIfLighthub(final InetAddress address) {
try {
RequestBody body = RequestBody.create(JSON, "{\"load\": {\"type\": \"is_lighthub_server\", \"data\": {}}");
Request request = new Request.Builder()
.url("http://" + address.getHostAddress())
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
final JSONObject myResponse = new JSONObject(response.body().string());
if((boolean)myResponse.get("is_lighthub")) {
onlineDeviceList.add(address);
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
} catch (JSONException jsonException) {
System.out.println(jsonException.getMessage());
}
}
The odd thing however is that that sample code, if provided with for example the address of my router, does receive the routers default html site ...
So, am I missing something? I fairly new to networking but a simple post request from the phone should be the same as from a python sample code, right?
Or is there a error in my java function?
Thank you guys in advance for the help!
If fixed it myself!
The mistake was that the python test client sended the json seperate, while the okhttp client sended both in one piece.
That made the server timeout while waiting for a second message ...
Related
Hello I am trying to begin an Android app in Kotlin. I have been trying to find a way to simply send and receive data from an android app to a simple HTTP server I made on my computer. The idea I am hoping is just make an app with a text box on it and a send button. When I press the send button it sends it to the simple http server (whether I use IP address or URL isn't Important)
This is just simply a proof of concept that I can make my own simple HTTP Server and get the app to send and receive from it.
I am essentially trying to do a server-client architecture between an android app and a computer. I can get this concept to work between two computer applications (python, java, C++ etc) but not how to do it in android. I keep looking for other answers on here but still come up short.
to be specific do I need enable certain features within the configurations file or a library that will allow the task to be done in the background?
Thank you for your help in advance.
If you want to connect of server for sending requests (POST and GET) then follow this code
public void callApi(String urls,String calledFor) {
try {
HttpGet httppost = new HttpGet(urls);
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response = httpclient.execute(httppost);
int status = response.getStatusLine().getStatusCode();
if (status == 200) {
HttpEntity entity = response.getEntity();
String data = EntityUtils.toString(entity);
JSONObject jsono = new JSONObject(data);
JSONObject json = jsono.getJSONObject("data");
}
} catch (ParseException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
}
ktor is a wonderful HTTP client that you can use for Android. It is developed by JetBrains, who are also authors of Kotlin, so it takes advantage of the features of the language:
You will need to add the ktor client for Android in build.gradle:
implementation "io.ktor:ktor-client-android:$ktor_version"
Then set up your client like this:
val client = HttpClient(Android) {
// setting these properties is optional; you don't need to if you wish to use the defaults
engine {
connectTimeout = 100_000
socketTimeout = 100_000
}
}
At last you can make HTTP requests like so (example from here): (the use method is to automatically close it at the end, make sure you read about releasing resources)
val resp: String = client.use {
val htmlContent = it.get<String>("https://en.wikipedia.org/wiki/Main_Page")
println(htmlContent)
// same as
val content: String = it.get("https://en.wikipedia.org/wiki/Main_Page")
content
}
Post Api request Using okHttp in Kotlin (Android)
follow steps
You will need to add the dependencies in build.gradle(:app)
implementation("com.squareup.okhttp3:okhttp:4.9.0")
request code here
fun runPostApi() {
var url = "https://reqres.in/api/users"
// add parameter
val formBody = FormBody.Builder().add("name", " Parker")
.build()
// creating request
var request = Request.Builder().url(url)
.post(formBody)
.build()
var client = OkHttpClient();
client.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
println(response.body?.string())
}
override fun onFailure(call: Call, e: IOException) {
println(e.message.toString())
}
})}
for more information link here below
https://square.github.io/okhttp/recipes/
In my app I make a simple get request via okhttp (this is simplified a bit, but you get the gist)
Request request = new Request.Builder()
.url(url)
.get()
.build();
try {
Response response = getOkHttpClient().newCall(request).execute();
if (response.isSuccessful()) {
return response.body().string();
} else {
return "";
}
}
catch (Exception e) {
Log.e(TAG, "Exception: ", e);
return "";
}
The url is a http url.
The api is my clients api and the call works fine outside their office network, unfortunately it does not work inside their office network.
Inside their network the response is 200 but there is no data attached to it (response.body().string() returns an empty string), however the request works just fine in the browser inside their network.
So what could the difference be between making the request inside their network from the app, versus inside their network in the browser? Could I spoof a browser user agent and that would fix it?
We do not know what was causing this, apparently their IT department made some sort of unrelated change to the network and now it works.
I have Web API service and Android Client to consume that service. Data parsing is working as expected. But now I have to transfer some documents/files to the web API. I have tried to pass in query parameter as string (after converting document to base64 string), which is working fine only for document having length of 2-3KB. But for large files (1-4MB) I get Socket timeout exception at android side I have tried with increasing the socket timeout for both android (okhttp client) and the web API from the C# code in controller.
please you can suggest me any other way that make it better. I know this is not the right way to pass files. I have max file size of 4-5 MB.
I am adding the code snippet of both Android and Web API. thanks in advance
C#:
[System.Web.Http.HttpPost]
public JsonResultModel SaveClaimDocument(string cd)
{
HttpContext.Current.Server.ScriptTimeout = 300;
.....
}
Android Code:
new Thread() {
#Override
public void run() {
super.run();
OkHttpClient client = SingleConnectionManager.getInstance().getConnectionWithHighTimeOut();
String url = "http://" + Constants.IP + "/api/ClaimDocuments/SaveClaimDocument?cd=" + file_base;
try {
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("test", cd)
.build();
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
//Set Before New Connection Request
System.setProperty("http.keepAlive", "false");
client.newCall(request).enqueue(new Callback() {
Edit:
Android Exception
Is there any way to send Upstream notification message through FCM from one android device to another devices connected with Firebase database.
I know that XMPP server can then receive the upstream messages and send the notifications to the other devices.To receive messages sent with the upstream API i need to implement an XMPP server but there is any other way???
Is there any way to send Upstream notification message through FCM
from one android device to another devices connected with Firebase
database?
Currently it's NOT possible to send messages directly from one device to another.
(or at least it's not possible without introducing a HUGE security vulnerability: more details below)
Full details:
Sending messages to a user device is a pretty serious action!
based on the payload a message can result in spam, phishing, execution of internal methods.
You want this operation to be allowed only be trusted entities, this is why the FCM send API requires the SERVER-API-KEY in the authentication header.
Adding the SERVER-API-KEY in your app code (or communicating it to the app in some other way) IS NOT SAFE. This because apk can be extracted, decompiled, inspected, executed on emulators, executed under debugging and so on.
The best way to implement this today: is to have some sort of server between the two devices:
[DeviceA] -- please send message to B --> [SERVER] -- fcmSendAPI --> [DeviceB]
The server can be as simple as a PHP page, or a more complex XMPP implementation.
An example in Node.js can be found here:
Sending notifications between devices with Firebase Database and Cloud Messaging
Finally, after 2 months of trying to maintain reliable server script myself, I suddenly found OneSignal. It's completely free, supports device-to-device push messages on iOS, Android, WP and browsers.
Hope, I won't get flag for promotion spam, but it's currently the only (and easiest) way to be completely "backendless".
Also, it's completely secure way. Nobody can send push unless he knows special OS user id, which you can store in Firebase Database protected by rules.
UPD: It's not a replacement for Firebase. It has only push service and nothing else
UPD2: Firebase now has Functions, and examples of it usage has sending FCM. You now don't need any other server or service. Read more in official samples https://github.com/firebase/functions-samples
After lots of try finally i got one solution and its work perfectly
Step 1 :Include two library.
compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.google.firebase:firebase-messaging:9.2.0'
Step 2 : In your MainActivity or from where you want to send notifications.
OkHttpClient mClient = new OkHttpClient();
String refreshedToken = "";//add your user refresh tokens who are logged in with firebase.
JSONArray jsonArray = new JSONArray();
jsonArray.put(refreshedToken);
Step 3: Create one async task which sends notifications to all devices.
public void sendMessage(final JSONArray recipients, final String title, final String body, final String icon, final String message) {
new AsyncTask<String, String, String>() {
#Override
protected String doInBackground(String... params) {
try {
JSONObject root = new JSONObject();
JSONObject notification = new JSONObject();
notification.put("body", body);
notification.put("title", title);
notification.put("icon", icon);
JSONObject data = new JSONObject();
data.put("message", message);
root.put("notification", notification);
root.put("data", data);
root.put("registration_ids", recipients);
String result = postToFCM(root.toString());
Log.d("Main Activity", "Result: " + result);
return result;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String result) {
try {
JSONObject resultJson = new JSONObject(result);
int success, failure;
success = resultJson.getInt("success");
failure = resultJson.getInt("failure");
Toast.makeText(MainActivity.this, "Message Success: " + success + "Message Failed: " + failure, Toast.LENGTH_LONG).show();
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(MainActivity.this, "Message Failed, Unknown error occurred.", Toast.LENGTH_LONG).show();
}
}
}.execute();
}
String postToFCM(String bodyString) throws IOException {
public static final String FCM_MESSAGE_URL = "https://fcm.googleapis.com/fcm/send";
final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
RequestBody body = RequestBody.create(JSON, bodyString);
Request request = new Request.Builder()
.url(Url.FCM_MESSAGE_URL)
.post(body)
.addHeader("Authorization", "key=" + "your server key")
.build();
Response response = mClient.newCall(request).execute();
return response.body().string();
}
Step 4 : Call in onclick of your button
btnSend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
sendMessage(jsonArray,"Hello","How r u","Http:\\google.com","My Name is Vishal");
}
});
I'm doing some tests using data to register a new user into my DB sending a json by post. The server returns a static string just for testing. I'm getting the proper response using the console and CURL
curl --data '' http://127.0.0.1:3000/api/user/register
<!DOCTYPE html><html><head><title>Express</title><link rel="stylesheet" href="/stylesheets/style.css"></head><body><h1>Express</h1><p>Welcome to Express</p></body></html>
and even with the POSTMAN extension sending a post request to that same URL I'm getting a proper response as well. But the code below is not getting any response from the server. What's missing?. It's not throwing any error and the json is correct after checking the debugger(it shouldn't matter but just in case anyone wants to know), just a regular string. The debugger fails on this line
Response response = client.newCall(request).execute();
private void createUser(String name, String email, String password){
//create JSON
final String userJson = formatUserAsJSON(name,email,password);
//send JSON
new AsyncTask<Void,Void,String>(){
#Override
protected String doInBackground(Void... voids) {
try {
//1.create client Object
OkHttpClient client = new OkHttpClient();
//2.Define request being sent to server
RequestBody postData=new FormBody.Builder()
.add("json",userJson)
.build();
Request request=new Request.Builder()
.url("http://127.0.0.1:3000/api/user/register")
.post(postData)
.build();
//3.Transport the request and wait for response to process next
Response response = client.newCall(request).execute();
String result = response.body().string();
return result;
} catch (Exception e) {
Log.d("error_connection","couldn't connect to the API");
return null;
}
}
}.execute();
}
I would appreciate some feedback on this problem. Thank you very much
EDIT: The server is not giving any response cause it's not being reached by the android app. I'm seeing in the ubuntu console and nodejs is not printing any message of any request.....
EDIT 2: In the Android manifest I have the user permission to access the network so it's not that.....
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
You are sending your JSON inside a FormBody
try this:
RequestBody postData = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), userJson);
I solved the problem putting the network IP(it also works if you put the Public internet IP, I've tried it) of the server cause 127.0.0.1 was refering to the Genymotion emulator itself, silly me :S. But that was the answer. Thanks everyone for their support.