kotlin not supporting Socket IO in android - android

I have two questions
1)My Project contain dagger2,retofit2,kotlin v1.0.21,rxJava2,OkHttp3 i want to implement SocketIO on my project how should i implement?
2) I try to several way but unable to connect socketIO so i try to sample code below given code but still unable to connect socket.. please help thanx in advance
package com.easymakers.myapplication
import android.os.Bundle
import android.support.design.widget.Snackbar
import android.support.v7.app.AppCompatActivity
import android.view.View
import io.socket.client.IO
import io.socket.client.Socket
import io.socket.emitter.Emitter
import kotlinx.android.synthetic.main.activity_main.*
import javax.net.ssl.SSLContext
class MainActivity : AppCompatActivity() {
private var socket : Socket? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
fab.setOnClickListener { view ->
connect(view)
// connect1()
}
}
private fun connect(view : View) {
val opts = IO.Options()
opts.port= 5000
opts.reconnection = false
// opts.query =
socket = IO.socket("https://192.170.1.21",opts)
socket?.connect()
?.on(Socket.EVENT_CONNECT, {
Snackbar.make(view, "Socket connected", Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
})
?.on(Socket.EVENT_DISCONNECT, { println("disconnected") })
}
private fun connect1(){
val sc = SSLContext.getInstance("SSL")
sc.init(null, null, null)
val opts = IO.Options()
opts.port = 3000
opts.secure = true
opts.forceNew = true
opts.reconnection = true
val socket = IO.socket("https://103.69.190.10",opts)
socket.on("connection", Emitter.Listener {
println("Connected")
socket.emit("foo", "hi")
socket.disconnect()
}).on("event", Emitter.Listener { }).on(Socket.EVENT_DISCONNECT, Emitter.Listener { })
socket.connect()
}
}

I used this library Socket.IO
With this code it was enough on method OnCreate. Even with an authentication token, but it's optional.
val opts = IO.Options()
opts.query = "token=XXXXXXXXX"
var socket = IO.socket("https://MYSERVERSOCKET.com", opts)
socket?.on(Socket.EVENT_CONNECT, {
Log.d("","==============================CONNECTED")
})?.on(Socket.EVENT_DISCONNECT, {
Log.d("","==============================OFF")
})
Start the connection
btnFindNext.setOnClickListener{
socket.connect()
}
and an example of emit
btnOtro.setOnClickListener{
val obj = JSONObject()
obj.put("var1", "5ab0a8931522f51fac37f5a1")
obj.put("var2", "91.8392894")
obj.put("var3", "-93.8392894")
socket.emit("SendData", obj)
}

1.1) https://github.com/socketio/socket.io-client-java
Gradle
Add it as a gradle dependency for Android Studio, in build.gradle:
compile ('io.socket:socket.io-client:1.0.0') {
// excluding org.json which is provided by Android
exclude group: 'org.json', module: 'json'
}
Socket.IO Server 1.x suppport
The current version of socket.io-client-java doesn't support socket.io
server 1.x. Please use socket.io-client-java 0.9.x for that instead.
1.2) in github you may see:
this link follow you to the updated version which support 1.x and upper server versions.
link: http://socketio.github.io/socket.io-client-java/project-summary.html
it have next dependency:
compile 'io.socket:socket.io-client:1.0.0-SNAPSHOT'
2) https://socket.io/get-started/chat/
Next step you must start node.js server
Integrating Socket.IO is composed of two parts:
1) A server that integrates with (or mounts on) the Node.JS HTTP Server:
socket.io
2) A client library that loads on the browser side:
socket.io-client During development, socket.io serves the client
automatically for us, as we’ll see, so for now we only have to install
one module:
npm install --save socket.io

Related

Hive VS Paho, Android Client. SSL issue with mosquitto broker

I am having issue connecting through SSL to my mosquitto broker. I have configured the broker correctly as it connects fine to my embedded device using lwIP mqtt client service. Sadly, i cannot use the same code on my android device.
I started down the rabbit hole...
Investigating potential clients for the android app; Paho client seemed the reasonable application software as it is part of the eclipse suite; as is the mosquitto broker. I spent many hours on the paho application, but i hit a wall that couldnt be overcome; the repo is just not up to date with the new androidX platform and the client would disconnect itself, i would get a proper certification and connection; the client would connect to the broker (YES!) but then Android would disconnect it and barf out the following error;
com.example.pahoclient: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
i investigated this; turns out this is not easily solved as the source code for the library is not easily modified. (At least not by someone with as little java experience as myself.) I tried to work through many "solutions" before giving up.
I then decided to try the HiveMQ client for android. While this seems a lot more current and active, I am unable to get the SSL cert to be received by my broker. its the same cert file but uses slightly modified functions. I will outline both codes here, starting with the successful client + broker connection.
my mosquitto broker is set up the following way;
listener 8883
#listener 1883
cafile certs/ca.crt
certfile certs/server.crt
keyfile certs/server.key
protocol mqtt
tls_version tlsv1.2
require_certificate false
allow_anonymous false
password_file certs/password
max_keepalive 5000
I load only the CA cert on the client. Hardcoded as follows;
package com.example.pahoclient
//import info.mqtt.android.service.MqttAndroidClient;
import android.app.Notification
import android.content.Context
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import org.eclipse.paho.android.service.MqttAndroidClient
import org.eclipse.paho.android.service.MqttService
import org.eclipse.paho.client.mqttv3.*
import java.security.KeyStore
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManagerFactory
class MQTTClient(context: Context,
serverURI: String = "ssl://example.com:8883",
clientID: String = "nonNull") {
companion object {
private const val TAG = "MQTTClient"
private val certString: String = "-----BEGIN CERTIFICATE-----\n" +
...
"rtVtZNE+luuMaDyGQYkNt3d1S3TWFVgd\n" +
"-----END CERTIFICATE-----\n"
private fun createSSLSocketFactory(): SSLSocketFactory? {
val cf = CertificateFactory.getInstance("X.509")
val cert = cf.generateCertificate(certString.byteInputStream()) as X509Certificate
val ks = KeyStore.getInstance(KeyStore.getDefaultType())
ks.load(null, null)
ks.setCertificateEntry("ca", cert)
val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
tmf.init(ks)
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, tmf.trustManagers, null)
return sslContext.socketFactory
}
}
private var mqttClient = MqttAndroidClient(context, serverURI, clientID)
fun connect(username: String = "...",
password: String = "...",
cbConnect: IMqttActionListener = defaultCbConnect,
cbClient: MqttCallback = defaultCbClient
) {
mqttClient.setCallback(cbClient)
val options = MqttConnectOptions()
options.userName = username
options.password = password.toCharArray()
options.isCleanSession = false
options.socketFactory = createSSLSocketFactory()
options.keepAliveInterval = 20
try {
mqttClient.connect(options, null, cbConnect)
} catch (e: MqttException) {
e.printStackTrace()
}
}
no client cert is used.
This is sufficient for my embedded device+lwIP; however, I would get the following output from the mosquitto broker when trying with the android device;
1676858808: mosquitto version 2.0.15 running
1676858841: New connection from 207.216.33.43:54808 on port 8883.
<Client disconnected>...
This was accompanied by the FLAG_MUTABLE... error in my android console..
com.example.pahoclient: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
This tells me the cert is correctly parsed and forwarded , however when i try to use a similiar setup with the HiveMQ client, the cert does not get through and i get the following output in the console;
1676863126: New connection from 207.216.33.43:54922 on port 8883.
1676863126: OpenSSL Error[0]: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
1676863126: Client <unknown> disconnected: Protocol error.
Here is the HiveMQ code;
package com.example.pahoclient
import android.R.attr.password
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.hivemq.client.mqtt.MqttClient
import com.hivemq.client.mqtt.mqtt3.Mqtt3AsyncClient
import java.security.KeyStore
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManagerFactory
private val certString: String = "-----BEGIN CERTIFICATE-----\n"+
...
"-----END CERTIFICATE-----"
class MainActivity : AppCompatActivity() {
private var client: Mqtt3AsyncClient? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
/**Call the socket factory**/
//CA stuff
val cf = CertificateFactory.getInstance("X.509")
val cert = cf.generateCertificate(certString.byteInputStream()) as X509Certificate
val ks = KeyStore.getInstance(KeyStore.getDefaultType())
ks.load(null, null)
ks.setCertificateEntry("ca", cert)
val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
tmf.init(ks)
client = MqttClient.builder()
.useMqttVersion3()
.identifier("my-mqtt-client-id")
.serverHost("******.com")
.serverPort(8883)
.sslConfig()
.keyManagerFactory(null)
.trustManagerFactory(tmf)
.applySslConfig()
.buildAsync()
client?.connectWith()
?.simpleAuth()
?.username("******")
?.password("*****".toByteArray())
?.applySimpleAuth()
?.send()
?.whenComplete { connAck, throwable ->
if (throwable != null) {
// handle failure
} else {
// setup subscribes or start publishing
}
}
}
}
Why would my cert fail with one implementation and not the other?
Is a client cert necessary with HiveMQ?
Both paho and hiveMQ use SSlSocketFactory, but HiveMQ has a requirement for KeyManagerFactory, which i have set to null.
Do I need to supply a client cert as well? I do have them created, but am unsure how i would go about implementing this.
Any suggestions?
I tried 2 different client, HiveMQ and Paho.
I tried to connect HiveMQ with CA cert loaded as well as a client cert loaded.

How to implement the checking of internet Android using RxJava/Rx Android Kotlin?

The answer for checking internet was posted back in 2014 in this post: https://stackoverflow.com/a/27312494/12359431
However, in one of the answer, there is this piece of code
fun hasInternetConnection(): Single<Boolean> {
return Single.fromCallable {
try {
// Connect to Google DNS to check for connection
val timeoutMs = 1500
val socket = Socket()
val socketAddress = InetSocketAddress("8.8.8.8", 53)
socket.connect(socketAddress, timeoutMs)
socket.close()
true
} catch (e: IOException) {
false
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
I have tried it by implementing the code above to my code at the bottom. However, it just crashes and I could not get any finding of the error as to why the app crash.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
/* Initialise Azure Service Adapter */
AzureServiceAdapter.Initialize(this)
hasInternetConnection().subscribe{hasInternet->
/*Call database and check phone number*/
Log.i("Logger", "Connected")}
/* Authentication */
authUser()
}
}
This is my implementations
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
Is there anything I'm lacking or I shouldn't add to my MainActivity File? Or a clue as to why my kotlin app crash ?
That's because you cant call this on main Thread.
Check if you added Internet permission in Manifest.
hasInternetConnection()
.subscribeOn(Schedulars.io())
.observeOn(AndroidSchedulars.mainThread()).subscribe{hasInternet->
/*Call database and check phone number*/
Log.i("Logger", "Connected")}

Confuse Library Server Sent Event for Android Client Nginx Push Stream

Our server is used to Nginx as webserver and add compile module of nginx_push_stream. Before used to push stream had used to Restful then changed to Websocket but WebSocket sometimes lost when the client or server had small bandwidth. In the 2019 year, from Websocekt to Server-Sent Event (SSE) / event-source such as event stream or text/event-stream to reduce loss both of client or server.
Please, anyone, have any idea for library event stream is able to use to the android client and iPhone client.
I have already used to Okhttp but there is not ready yet used event stream, RxSSE is not able to use in Android no response at all.
I hope that next year OkHttp is already updated OkHttp-EventSource for Android Client also iPhone Client
After 3 days, Struggling had search library for supporting SSE of Android client. Then, i found this blog Accessing SSE help me a lot to implementation SSE, also this the library SSE
This sample implementation SSE in kotlin version, even thought library is java version.
1. Preparing for event handler source
interface DefaultEventHandler : EventHandler {
#Throws(Exception::class)
override fun onOpen() {
Log.i("open","open")
}
#Throws(Exception::class)
override fun onClosed() {
Log.i("close","close")
}
#Throws(Exception::class)
override fun onMessage(event: String, messageEvent: MessageEvent) {
Log.i("event", messageEvent.data)
}
override fun onError(t: Throwable) {
Log.e("error", t.toString())
}
override fun onComment(comment: String) {
Log.i("event", comment)
}
}
class MessageEventHandler : DefaultEventHandler {
override fun onMessage(event: String, messageEvent: MessageEvent) {
super.onMessage(event, messageEvent)
val data = messageEvent.data
Log.i("message", data)
}
}
2. implementation event source
import java.net.URI
import java.util.concurrent.TimeUnit
.....
fun initEventSource(url: String) {
val eventHandler = MessageEventHandler()
try {
val eventSource: EventSource = EventSource.Builder(handler, URI.create(url))
.reconnectTimeMs(3000)
.build()
eventSource.start()
TimeUnit.SECONDS.sleep(10)
} catch (e: Exception) {
Log.e("error", e.toString())
}
}
I hope this would be alternative method protocol from client to server than used RESTfull or Websocket. When server always sent data to client without need request from client as stream.
I have added a gist of using library SSE https://gist.github.com/subhanshuja/9079ec9df0927b1da26ee57cf9da1f26.

How to send and receive strings through TCP connection using kotlin

I have a TCP Server on Windows, and I want to send and receive text strings between the server and my Android device.
I spent alot of time searching for an example using Kotlin but I didn't find any useful code, so I'm now only able to create the socket and connect.
fun connect() {
try{
val soc = Socket("192.168.1.5", 1419)
val dout = DataOutputStream(soc.getOutputStream())
dout.writeUTF("1")
dout.flush()
dout.close()
soc.close()
}
catch (e:Exception){
e.printStackTrace()
}
}
You can check this simple example. Hope it'll help you!
Server:
fun main() {
val server = ServerSocket(9999)
println("Server running on port ${server.localPort}")
val client = server.accept()
println("Client connected : ${client.inetAddress.hostAddress}")
val scanner = Scanner(client.inputStream)
while (scanner.hasNextLine()) {
println(scanner.nextLine())
break
}
server.close()
}
Client:
fun main() {
val client = Socket("127.0.0.1", 9999)
client.outputStream.write("Hello from the client!".toByteArray())
client.close()
}
You can also do it with ktor, it's a kotlin based asynchronous framework. It uses coroutines natively which allow concurrency.
Use Kotlin 1.4 and ktor 1.6.0, add it to your build.gradle.kts:
plugins {
kotlin("jvm") version "1.4.32"
}
dependencies {
implementation("io.ktor:ktor-server-netty:1.6.0")
implementation("io.ktor:ktor-network:1.6.0")
}
Then you can use the sockets, it's still a bit experimental but it's getting there, with newer version ktor-network is now necessary.
Here is the code:
Server:
suspend fun server() {
val server = aSocket(ActorSelectorManager(Executors.newCachedThreadPool().asCoroutineDispatcher())).tcp()
.bind(InetSocketAddress("127.0.0.1", 2323))
println("Server running: ${server.localAddress}")
val socket = server.accept()
println("Socket accepted: ${socket.remoteAddress}")
val input = socket.openReadChannel()
val output = socket.openWriteChannel(autoFlush = true)
val line = input.readUTF8Line()
println("received '$line' from ${socket.remoteAddress}")
output.writeFully("$line back\r\n".toByteArray())
}
Client:
suspend fun client() {
val socket = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp()
.connect(InetSocketAddress("127.0.0.1", 2323))
val input = socket.openReadChannel()
val output = socket.openWriteChannel(autoFlush = true)
output.writeFully("hello\r\n".toByteArray())
println("Server said: '${input.readUTF8Line()}'")
}
Run them both:
fun main() {
CoroutineScope(Dispatchers.Default).launch { server() }
runBlocking { client() }
}
When you run them, the client will send a message, the server will respond and you should see something like this:
Server running: /127.0.0.1:2323
Socket accepted: /127.0.0.1:56215
received 'hello' from /127.0.0.1:56215
Server said: 'hello back'
Find more example on their documentation simple echo server
There are 2 important things based on my experiment:
get permission in AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
create the socket from a background thread, the following works for me:
Executors.newSingleThreadExecutor().execute {
val socket = Socket("192.168.0.15", 50000)
val scanner = Scanner(socket.getInputStream())
val printWriter = PrintWriter(socket.getOutputStream())
while (scanner.hasNextLine()) {
Log.d(TAG, "${ scanner.nextLine() }")
}
}
This is the source code in GitHub.
There is a video of my experiment.

Testing RxWorker for WorkManager

I want to try out work manager for the first time. I am used to rxJava so I decided to implement my work manager using RxWorker. But the testing aspect is giving me headache.Basically, the work manager checks firebase to get latest changes to latest changes to particular document (This is not the best use case I know).But the problem is in the test returns without waiting for success or failure.It returns when the work manager is still running.
This is my work manager implementation
class MidiSyncWorker(context: Context, params: WorkerParameters) : RxWorker(context, params) {
override fun createWork(): Single<Result> {
return Injection.provideSharePrefsRepo.midiArchiveVersion()
.flatMapObservable { currentVersion ->
Injection.provideOnlineRepo.latestMidiArchive()
.filter { onlineMidi -> onlineMidi.version > currentVersion }
}.firstOrError()
.map { onlineMidi ->
val outputData = Data.Builder()
.putString(KEY_FIREBASE_ARCHIVE_PATH, onlineMidi.url)
Result.success(outputData.build()) }
}
.onErrorReturn { Result.failure() }
}
This is my test
fun midiSyncVersionCheck_success_onlineVersionDiffersFromLocalVersion() {
// create request
val request = OneTimeWorkRequestBuilder<MidiSyncWorker>()
.build()
wmRule.workManager.enqueue(request).result.get()
val workInfo = wmRule.workManager.getWorkInfoById(request.id).get(10, TimeUnit.SECONDS)
assertThat(workInfo.state, `is`(WorkInfo.State.SUCCEEDED))
}
I expected the test to wait until workmanager returns success or failure. But it returns while work manager is still running
java.lang.AssertionError:
Expected: is <SUCCEEDED>
but: was <RUNNING>
WorkManager makes available a couple of ways to test your Worker classes. You can find all the details on WorkManager Testing documentation page.
The original WorkManagerTestInitHelper only supports Worker classes, meanwhile, the newly introduce in (WorkManager v2.1.0-alpha01) TestListenableWorkerBuilder can be used to test both ListenableWorker classes and the other classes that derives from it (like CoroutineWorker and RxWorker.
In your particular case, you should be able to do:
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.work.ListenableWorker.Result
import androidx.work.testing.TestListenableWorkerBuilder
import org.hamcrest.CoreMatchers.`is`
import org.junit.Assert.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
#RunWith(JUnit4::class)
class MyWorkTest {
private lateinit var context: Context
#Before
fun setup() {
context = ApplicationProvider.getApplicationContext()
}
#Test
fun testMidiSyncWorker() {
// Get the ListenableWorker
val worker = TestListenableWorkerBuilder<MidiSyncWorker>(context).build()
// Start the work synchronously
val result = worker.startWork().get()
assertThat(result, `is`(Result.success()))
}
}
In this way you're calling synchrously your worker class.
In this case you need to use the as a test dependency in your build.gradle file:
def work_version = "2.1.0-alpha02"
androidTestImplementation "androidx.work:work-testing:$work_version"
You can find a similar, complete, sample (for a CoroutineWorker), on the Kotlin's coroutine Codelab.

Categories

Resources