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?
Related
I am trying to connect my server deployed in heroko. But as you can see in the code below, i can't connect to my server. I tried it in web (with just html,javascript) and i am able to connect.
But in android using kotlin, i can't connect. I tried adding android:usesCleartextTraffic but doesn't work. Did anyone faced this issue ?
class MainActivity : AppCompatActivity() {
private val TAG = "mytag"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val socket = IO.socket("https://nesib-api.herokuapp.com/")
socket.connect()
Log.d(TAG, "socked connected : "+ socket.connected()) // always returns false
socket.on(Socket.EVENT_CONNECT){
Log.d(TAG, "connected ! ") // this method never fires
}
}
}
Write <uses-permission android:name="android.permission.INTERNET" /> in your AndroidManifest.xml.
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")}
In my Android app I have to show a list of available services on the network published by another machine (RPi 3B con Raspbian Stretch) using avahi 0.6.32 (Bonjour/zeroconf daemon for Linux). I obtain the list on Android phone using NsdManager.
But, during testing, I'm getting a strange behavior: when I switch WiFi off and back on in the phone, most of the times the services are discovered, then immediately lost and then rediscovered (instead of just discovered once) and all of this in less than a second.
This causes the list of services to briefly appear on screen, then disappear and finally reappear almost immediately, but it's still very noticeable. And it forces the services to be discovered and resolved twice. As I expect to have lots of phones connecting to several services in the same LAN, I want to avoid overloading the network.
I'm not sure if I'm doing something wrong or it's just the way NsdManager works on Android. To reduce possible sources of problem, I commented out the lines that resolve the services (leaving only the log messages) but the problem persisted (more then half of the times).
How can I solve it?
Sample extract from Logcat:
2019-09-26 04:33:50.262 27300-27420/com.example.myapp D/NsdHelper$initializeDiscoveryListener: Service discovery success: name: MyService-1490247, type: _mytype._tcp., host: null, port: 0, txtRecord:
2019-09-26 04:33:50.879 27300-27420/com.example.myapp D/NsdHelper$initializeDiscoveryListener: Service lost: name: MyService-1490247, type: _mytype._tcp., host: null, port: 0, txtRecord:
2019-09-26 04:33:50.970 27300-27420/com.example.myapp D/NsdHelper$initializeDiscoveryListener: Service discovery success: name: MyService-1490247, type: _mytype._tcp., host: null, port: 0, txtRecord:
I'm testing on a Samsung Note 8 with Android O. I tried with 2 different WiFi routers and the behavior is the same.
I'm using the following NsdHelper class (in Kotlin):
import android.content.Context
import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
import timber.log.Timber
import java.util.*
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.collections.ArrayList
abstract class NsdHelper(val context: Context) {
// Declare DNS-SD related variables for service discovery
val nsdManager: NsdManager? = context.getSystemService(Context.NSD_SERVICE) as NsdManager?
private var discoveryListener: NsdManager.DiscoveryListener? = null
private var resolveListener: NsdManager.ResolveListener? = null
private var resolveListenerBusy = AtomicBoolean(false)
private var pendingNsdServices = ConcurrentLinkedQueue<NsdServiceInfo>()
var resolvedNsdServices: MutableList<NsdServiceInfo> =
Collections.synchronizedList(ArrayList<NsdServiceInfo>())
companion object {
// Type of services to look for
const val NSD_SERVICE_TYPE: String = "_mytype._tcp."
// Services' Names must start with this
const val NSD_SERVICE_NAME: String = "MyService-"
}
// Initialize Listeners
fun initializeNsd() {
// Initialize only resolve listener
initializeResolveListener()
}
// Instantiate DNS-SD discovery listener
// used to discover available Sonata audio servers on the same network
private fun initializeDiscoveryListener() {
Timber.d("Initialize DiscoveryListener")
// Instantiate a new DiscoveryListener
discoveryListener = object : NsdManager.DiscoveryListener {
override fun onDiscoveryStarted(regType: String) {
// Called as soon as service discovery begins.
Timber.d("Service discovery started: $regType")
}
override fun onServiceFound(service: NsdServiceInfo) {
// A service was found! Do something with it
Timber.d("Service discovery success: $service")
if ( service.serviceType == NSD_SERVICE_TYPE &&
service.serviceName.startsWith(NSD_SERVICE_NAME) ) {
// Both service type and service name are the ones we want
// If the resolver is free, resolve the service to get all the details
if (resolveListenerBusy.compareAndSet(false, true)) {
nsdManager?.resolveService(service, resolveListener)
} else {
// Resolver was busy. Add the service to the list of pending services
pendingNsdServices.add(service)
}
} else {
// Not our service. Log message but do nothing else
Timber.d("Not our Service - Name: ${service.serviceName}, Type: ${service.serviceType}")
}
}
override fun onServiceLost(service: NsdServiceInfo) {
Timber.d("Service lost: $service")
// If the lost service was in the queue of pending services, remove it
synchronized(pendingNsdServices) {
val iterator = pendingNsdServices.iterator()
while (iterator.hasNext()) {
if (iterator.next().serviceName == service.serviceName) iterator.remove()
}
}
// If the lost service was in the list of resolved services, remove it
synchronized(resolvedNsdServices) {
val iterator = resolvedNsdServices.iterator()
while (iterator.hasNext()) {
if (iterator.next().serviceName == service.serviceName) iterator.remove()
}
}
// Do the rest of the processing for the lost service
onNsdServiceLost(service)
}
override fun onDiscoveryStopped(serviceType: String) {
Timber.d("Discovery stopped: $serviceType")
}
override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) {
Timber.e("Start Discovery failed: Error code: $errorCode")
stopDiscovery()
}
override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) {
Timber.e("Stop Discovery failed: Error code: $errorCode")
nsdManager?.stopServiceDiscovery(this)
}
}
}
// Instantiate DNS-SD resolve listener to get extra information about the service
private fun initializeResolveListener() {
Timber.d("Initialize ResolveListener")
resolveListener = object : NsdManager.ResolveListener {
override fun onServiceResolved(service: NsdServiceInfo) {
Timber.d("Service Resolve Succeeded: $service")
// Register the newly resolved service into our list of resolved services
resolvedNsdServices.add(service)
// Process the newly resolved service
onNsdServiceResolved(service)
// Process the next service waiting to be resolved
resolveNextInQueue()
}
override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) {
// Called when the resolve fails. Use the error code to debug.
Timber.e("Resolve failed: $serviceInfo - Error code: $errorCode")
// Process the next service waiting to be resolved
resolveNextInQueue()
}
}
}
// Start discovering services on the network
fun discoverServices() {
// Cancel any existing discovery request
stopDiscovery()
initializeDiscoveryListener()
// Start looking for available audio channels in the network
nsdManager?.discoverServices(NSD_SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener)
}
// Stop DNS-SD service discovery
fun stopDiscovery() {
if (discoveryListener != null) {
Timber.d("stopDiscovery() called")
try {
nsdManager?.stopServiceDiscovery(discoveryListener)
} finally {
}
discoveryListener = null
}
}
// Resolve next NSD service pending resolution
private fun resolveNextInQueue() {
// Get the next NSD service waiting to be resolved from the queue
val nextNsdService = pendingNsdServices.poll()
if (nextNsdService != null) {
// There was one. Send to be resolved.
nsdManager?.resolveService(nextNsdService, resolveListener)
} else {
// There was no pending service. Release the flag
resolveListenerBusy.set(false)
}
}
// Function to be overriden with custom logic for new service resolved
abstract fun onNsdServiceResolved(service: NsdServiceInfo)
// Function to be overriden with custom logic for service lost
abstract fun onNsdServiceLost(service: NsdServiceInfo)
}
On the init block of the View Model, I start the service discovery:
class MyViewModel(application: Application) : AndroidViewModel(application) {
// Get application context
private val myAppContext: Context = getApplication<Application>().applicationContext
// Declare NsdHelper object for service discovery
private val nsdHelper: NsdHelper? = object : NsdHelper(myAppContext) {
override fun onNsdServiceResolved(service: NsdServiceInfo) {
// A new network service is available
// Update list of available services
updateServicesList()
}
override fun onNsdServiceLost(service: NsdServiceInfo) {
// A network service is no longer available
// Update list of available services
updateServicesList()
}
}
// Block that is run when the view model is created
init {
Timber.d("init block called")
// Initialize DNS-SD service discovery
nsdHelper?.initializeNsd()
// Start looking for available audio channels in the network
nsdHelper?.discoverServices()
}
// Called when the view model is destroyed
override fun onCleared() {
Timber.d("onCleared called")
nsdHelper?.stopDiscovery()
super.onCleared()
}
private fun updateServicesList() {
// Put the logic here to show the services on screen
return
}
}
Note: Timber is a logging utility, almost a direct replacement for standard Log commands, but easier to use.
I have an app that communicates with a server during sockets.
first I did this:
val sock = Socket("192.168.1.108", 5000)
and the app crashed because of the error: "android.os.NetworkOnMainThreadException", I read about it and I found a solution for that error and the solution is to create a syncTask as an inner class, and this is what I did:
class randomChat : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.chat_show)
HandleServer().execute()
}
inner class HandleServer: AsyncTask<String, String, String>() {
override fun doInBackground(vararg p0: String?): String {
val sock = Socket("192.168.1.108", 5000)
sock.getInputStream()
sock.use {
it.outputStream.write("hello socket world".toByteArray())
}
return "Good"
}
}
}
and that fixed the error, But did not fix my needs...
basically, my needs are to have a conversion between the server and the user who use the app, the user will have an editText view and a button to send the data to the server and a textView that always change based on server data.
So:
What I need to have is:
open socket that receives data all the time from the server and updates a view in the activity
I need to be able to have an editText in the activity that by user click it's send data to the server (with the socket)
Thank you very much !!!!!
You can implement on ongoing socket like this, and just call the sendDataToNetwork() method for the editText
Example: Android bi-directional network socket using AsyncTask
There are better ways to manage threads or use libraries like RxJava, but for a basic simple implementation the above should work.
Instead of bothering with the AsyncTask, I highly recommend you to use coroutines instead:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.chat_show)
launch (CommonPool) {
val sock = Socket("192.168.1.108", 5000)
sock.use {
it.outputStream.write("hello socket world".toByteArray())
withContext(UI) {
// update the view
}
// more socket ops
withContext(UI) {
// update the view again
}
}
}
}
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?