Send text using Android Studio and MQTT broker - android

I'm using Kotlin. You can also answer in Java.
Currently, I want to send a message from Android Studio to the MQTT broker.
However, if the activity is not connected, the message is delivered, but when the activity is connected, an error occurs that cannot connect to the server.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button2 = findViewById<Button>(R.id.button2)
button2.setOnClickListener {
connect()
}
}
private fun connect() {
val topic = "/example"
val content = "Send Message"
val qos = 2
val broker = "tcp://localhost:1883"
val persistence = MemoryPersistence()
try {
val Client = MqttClient(broker, MqttClient.generateClientId(), persistence)
val con = MqttConnectOptions()
con.isCleanSession = true
println("Connecting to broker: $broker")
Client.connect(con)
println("Connected")
println("Send Message: $content")
val message = MqttMessage(content.toByteArray())
message.qos = qos
Client.publish(topic, message)
Client.disconnect()
exitProcess(0)
Toast.makeText(this, "success", Toast.LENGTH_SHORT ).show()
} catch (ex: MqttException) {
ex.printStackTrace()
Toast.makeText(this, "Fail", Toast.LENGTH_SHORT ).show()
}
}
}
The above code was created by referring to several examples.
W/System.err: Unable to connect to server (32103) - java.net.ConnectException: Connection refused
at org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule.start(TCPNetworkModule.java:80)
at org.eclipse.paho.client.mqttv3.internal.ClientComms$ConnectBG.run(ClientComms.java:724)
at java.lang.Thread.run(Thread.java:762)
W/System.err: Caused by: java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:334)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:196)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:178)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:356)
at java.net.Socket.connect(Socket.java:586)
at org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule.start(TCPNetworkModule.java:74)
... 2 more
The above information is in error.
Here, if you execute the "fun connect()" function alone, a message arrives.
Can you tell me where I was wrong when I tried to send a message when I click the button on the function to be executed?

When the code runs on the phone (or in the emulator) the hostname localhost will resolve to the phone (or emulator) not the machine the MQTT broker is running on (I assume your development machine).
If running in the emulator you can use the IP address 10.0.2.2 but this should only be used for local testing.
You need to use the IP address of your development machine on the network to test from a phone attached to the same network as the development machine.
For further testing/deployment you will most likely need a broker that is publicly accessible from the internet.

Related

Error: java.io.IOException: read failed, socket might closed or timeout, read ret: -1 when connecting to BLE module (Kotlin)

I am trying to connect to an RN4870 BLE module using my Kotlin app, but keep getting an error when trying to connect a socket. I am able to pair my phone and the BLE module, and verify in kotlin they they are bonded correctly, but when I try to run these two lines.
btSocket = RN4780Device.createInsecureRfcommSocketToServiceRecord(RN4870_UUID)
btSocket.connect()
I always get this error: java.io.IOException: read failed, socket might closed or timeout, read ret: -1
Note: I am able to send and receive data using another app (SmartData iOS app) so I know the Bluetooth FW is working correctly.
After doing a lot of research, I can only assume that there is an issue with my UUID. I'm not sure if I should use the generic one I am now, or somehow get a UUID that is being used by the RN4870 module.
Main Activity:
class MainActivity : AppCompatActivity() {
lateinit var btAdapter: BluetoothAdapter
lateinit var RN4780Device: BluetoothDevice
lateinit var btSocket: BluetoothSocket
lateinit var btService: MyBluetoothService
#SuppressLint("MissingPermission")
#RequiresApi(Build.VERSION_CODES.S)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Initialize bluetooth adapter
val btManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
btAdapter = btManager.adapter
// Start home activity if connected to Bluetooth module
btn_cont.setOnClickListener {
if (checkPaired()) {
// Connect client socket
try {
btSocket = RN4780Device.createInsecureRfcommSocketToServiceRecord(RN4870_UUID)
btSocket.connect()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}
#SuppressLint("MissingPermission")
private fun checkPaired() : Boolean {
// Find RN4780 device and check bond state
val device: BluetoothDevice = btAdapter.getRemoteDevice(RN4780_ADDRESS)
if (device.bondState != BluetoothDevice.BOND_BONDED) {
Toast.makeText(this, "RN4870 BLE Module is not Connected", Toast.LENGTH_LONG).show()
return false
}
RN4780Device = device
return true
}
}
Constants
object Constants {
var RN4780_ADDRESS = "04:91:62:A1:7D:0D"
var RN4870_UUID = UUID.fromString("49535343-8841-43F4-A8D4-ECBE34729BB3")
}
Full StackTrace
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.geoboard, PID: 1294
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:958)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:958) 
Caused by: java.io.IOException: read failed, socket might closed or timeout, read ret: -1
at android.bluetooth.BluetoothSocket.readAll(BluetoothSocket.java:758)
at android.bluetooth.BluetoothSocket.readInt(BluetoothSocket.java:772)
at android.bluetooth.BluetoothSocket.connect(BluetoothSocket.java:404)
at com.example.geoboard.MainActivity.onCreate$lambda-0(MainActivity.kt:54)
at com.example.geoboard.MainActivity.$r8$lambda$z_JWuogCboqpr2BmqKXIU9Cwb9g(Unknown Source:0)
at com.example.geoboard.MainActivity$$ExternalSyntheticLambda2.onClick(Unknown Source:2)
at android.view.View.performClick(View.java:7471)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1194)
at android.view.View.performClickInternal(View.java:7448)
at android.view.View.access$3600(View.java:813)
at android.view.View$PerformClick.run(View.java:28408)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:250)
at android.app.ActivityThread.main(ActivityThread.java:7755)
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:958)
I have been looking at different stack overflow posts for hours and was unable to find a solution. Thanks for any help!
Turns out that you can't connect to Low Energy Bluetooth with a socket. Instead, see https://developer.android.com/guide/topics/connectivity/bluetooth/connect-gatt-server

Tcp/Ip how do i request server to accept my request to join your server on Android?

I'm working on simple TCP/IP application on Android that sends/receives packages.
I have set up code to find to host and join network. My code works where client is able to auto join the server when it's available, however, when i discover a open server, i want to send request/message to server that I want to join your server.
here's my code.
// Activity to hold our server socket
serverSocket: ServerSocket? = null
// Create a listen socket
val port: Int
serverSocket = ServerSocket(0).also { socket ->
// Store the chosen port.
port = socket.localPort
}
Thread(Runnable {
while (serverSocket != null) {
try {
serverSocket?.accept()?.let {
Log.d("ServerSocket", "accepted client")
// client found, connect to client automatically
connectToClient()
}
} catch (e: SocketException) {
break
}
}
}).start()
nsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener)
than for client i'm using this to discover server
nsdManager.discoverServices("_LocalNetworkingApp._tcp.", NsdManager.PROTOCOL_DNS_SD, discoveryListener)
private val discoveryListener = object : NsdManager.DiscoveryListener {
...
override fun onServiceFound(service: NsdServiceInfo) {
Log.d(TAG, "Service found ${service.serviceName}")
nsdManager.resolveService(service, resolveListener)
}
}
// in resolverServices' `onServiceResolved` function is where i'm connected to the server

How to keep an Android grpc client with server streaming connection alive?

I have a grpc-js server and a Kotlin for Android client that makes a server streaming call. This is the GRPCService class.
class GRPCService {
private val mChannel = ManagedChannelBuilder
.forAddress(GRPC_HOST_ADDRESS, GRPC_HOST_PORT)
.usePlaintext()
.keepAliveTime(10, TimeUnit.SECONDS)
.keepAliveWithoutCalls(true)
.build()
val asyncStub : ResponderServiceGrpc.ResponderServiceStub =
ResponderServiceGrpc.newStub(mChannel)
}
And the method is called from a foreground service.
override fun onCreate() {
super.onCreate()
...
startForeground(MyNotificationBuilder.SERVICE_NOTIFICATION_ID, notificationBuilder.getServiceNotification())
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val userId = sharedPreferencesManager.getInt(SharedPreferencesManager.USER_ID)
val taskRequest = Responder.TaskRequest.newBuilder()
.setUserId(userId)
.build()
grpcService.asyncStub.getTasks(taskRequest, object :
StreamObserver<Responder.TaskResponse> {
override fun onCompleted() {
Log.d("grpc Tasks", "Completed")
}
override fun onError(t: Throwable?) {
Log.d("grpc error cause", t?.cause.toString())
t?.cause?.printStackTrace()
Log.d("grpc error", "AFTER CAUSE")
t!!.printStackTrace()
}
override fun onNext(value: Responder.TaskResponse?) {
if (value != null) {
when (value.command) {
...
}
}
}
})
return super.onStartCommand(intent, flags, startId)
}
The connection opens and stays open for about a minute of no communication and then fails with the following error.
D/grpc error cause: null
D/grpc error: AFTER CAUSE
io.grpc.StatusRuntimeException: INTERNAL: Internal error
io.grpc.Status.asRuntimeException(Status.java:533)
io.grpc.stub.ClientCalls$StreamObserverToCallListenerAdapter.onClose(ClientCalls.java:460)
io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:426)
io.grpc.internal.ClientCallImpl.access$500(ClientCallImpl.java:66)
io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.close(ClientCallImpl.java:689)
io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.access$900(ClientCallImpl.java:577)
io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInternal(ClientCallImpl.java:751)
io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:740)
io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
The grpc-js server is created with the following options.
var server = new grpc.Server({
"grpc.http2.min_ping_interval_without_data_ms" : 10000,
"grpc.keepalive_permit_without_calls" : true,
"grpc.http2.min_time_between_pings_ms" : 10000,
"grpc.keepalive_time_ms" : 10000,
"grpc.http2.max_pings_without_data" : 0,
'grpc.http2.min_ping_interval_without_data_ms': 5000
});
I never received the too many pings error either.
I noticed that if there is periodic communication (like the server pinging the client with a small amount of data every 30s or so) through this connection then I don't get the error and the connection stays open for as long as the pinging continues (tested for 2 days).
How do I keep the connection open without resorting to periodically pinging the client?
The managed channel has a property called keepAliveWithoutCalls which has a default value of false as seen here. If this is not set to true then the keepAlive will not happen if there are no current active calls happening. You would need to set this like so:
private val mChannel = ManagedChannelBuilder
.forAddress(GRPC_HOST_ADDRESS, GRPC_HOST_PORT)
.usePlaintext()
.keepAliveTime(30, TimeUnit.SECONDS)
.keepAliveWithoutCalls(true)
.build()
There is a possibility that you will have to do some other settings on the server as well to have the connection stay open without any data passing. You might get an error on the server saying "too many pings". This happens because there are some other settings GRPC needs. I am not sure exactly how to achieve this with a JS server but it shouldn't be too difficult. These settings include:
GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS
Minimum allowed time between a server receiving successive ping frames without sending any data/header/window_update frame.
And this one:
GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS
Minimum time between sending successive ping frames without receiving any data/header/window_update frame, Int valued, milliseconds.
And this one:
GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS
Is it permissible to send keepalive pings without any outstanding streams.
There is a Keepalive User Guide for gRPC which I suggest you read through to understand how gRPC should keep connections open. This is the core standard that all server and client implementations should follow, but I have noticed this is not always the case. You can have a look at a previous but similar question I asked a while back here.
Have you tried the ManagedChannelBuilder.keepAliveTime setting (https://github.com/grpc/grpc-java/blob/master/api/src/main/java/io/grpc/ManagedChannelBuilder.java#L357) ? I am assuming it will work in the middle of a server streaming call.

Sockets not understood

What I am trying to make is a local chat app (i.e, over local network, i.e. without internet only i.e. hotspot wifi based on android).I studied about sockets and tried to implement using https://github.com/socketio/socket.io-client-java I tried to make use of mainly socket.emit and socket.on. Both of my devices are connected on local network so can I do this
class MainActivity : AppCompatActivity()
{
var TAG = "TCPClient"; //For debugging, always a good idea to have defined
var serverIp = "192.168.0.102";
var startTime = 0L;
var serverPort = 5000;
lateinit var socket:Socket
var onMessageReceived=object:Emitter.Listener
{
override fun call(vararg args: Any?)
{
runOnUiThread {
editText2.setText(args[0].toString())
Toast.makeText(this#MainActivity,"called",Toast.LENGTH_SHORT).show()
}
}
}
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var SOCKET_URL="http://192.168.0.102"
socket= IO.socket(SOCKET_URL)
button.setOnClickListener {
socket.emit("messageSent",editText.text.toString())
Toast.makeText(this#MainActivity,"button",Toast.LENGTH_SHORT).show()
}
socket.on("messageSent",onMessageReceived)
socket.connect()
}
override fun onDestroy()
{
socket.disconnect()
super.onDestroy()
}
}
What I was trying whatever is the text written in editext1(in one phone with ip 192.68.0.102) is displayed in edittex2 (of both devices (one with ip 192.168.0.102 and the other devices on local network(192.168.0.100) since I ran the same app on both devices)) when button is pressed.What I am doing wrong? I would have made this app by hosting rest service on one of the devices and accessing it from all the connected devices in the network but the main problem I was facing is that if I do so I would have to request the rest api again and again in a loop in a thread to check for a new message.
Don't all the connected devices listen when we use socket.on?

Exstablishing a simple socket connect with localhost and android emulator

I am building an application for student attendance for my college. Where in the attendance is being taken via the college network. Each class room has a separate 'routers', and the system detects if student has present in the range.
For the same, what I am trying to do is establish a socket connection, and every time that it connects, the server in return receives the WiFi information to judge the class room.
To establish, I first made this index.js file, here is the code for it
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function(req,res){
res.sendFile(__dirname+'/index.html');
})
http.listen(4040, function(){
console.log("Server is listening on port 4040");
})
io.on('connection', function(socket){
console.log('connect', "A user connected "+ socket.id)
io.on('disconnect', function(socket){
console.log("disconnect A user disconnected" + socket.id)
})
})
Problem: The problem is that, 'connection' event is not working. It says that it's listening but nothing is getting console logged. I am using an Emulator as client and a local XAMPP server. However, the 'connection' event does work when I use browser as a client.
Here is my client side code:
private val socket = Socket()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_student_home)
setSupportActionBar(student_toolbar)
val toggle = ActionBarDrawerToggle(
this, student_drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
student_drawer_layout.addDrawerListener(toggle)
toggle.syncState()
student_nav_view.setNavigationItemSelectedListener(this)
socket.connect()
}
override fun onResume() {
super.onResume()
socket.connect()
}
override fun onStart() {
super.onStart()
socket.connect()
}
override fun onDestroy() {
super.onDestroy()
socket.disconnect()
}//[End : onDestroy]
fun Socket() : Socket{
val socket : Socket
try {
socket = IO.socket("http://localhost:4040")
} catch (e: URISyntaxException) {
throw RuntimeException(e)
}
return socket;
}
try to change "localhost" to your machine's ip address like "http://192.168.1.100:4040". can you post screenshot of backtrace?

Categories

Resources