Sending and Receiving strings with Sockets in android - android

I have simple client and server socket application connecting each other based on this code. The problem is i can send and get values but when client getting answer from server continue to send message.
Where is my problem?
here is server : My server socket is in Service.I execute it when service starts
class SocketService: Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
thread { ClientHandler().run() }
return START_STICKY
}
internal inner class ClientHandler() : Runnable{
override fun run() {
val server = ServerSocket(5000)
val client = server.accept()
var reader = BufferedReader(InputStreamReader(client.getInputStream()))
var writer = PrintWriter(client.getOutputStream())
try {
receiveString = reader.readLine()
while(receiveString != "")
{
println(receiveString)
writer.write("hello from server" + "\n")
writer.flush()
}
writer.close();
reader.close();
server.close();
client.close();
} catch (ex: Exception) {
Timber.e("$TAG $formatted $ex")
}
}
}
Here my client :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
connectToPaymentService()
}
fun connectToPaymentService(){
thread { ThreadopenPort().run() }
}
internal inner class ThreadopenPort : Runnable {
override fun run() {
val socket: Socket
try {
socket = Socket(SERVER_IP, SERVER_PORT)
output = PrintWriter(socket.getOutputStream())
input = BufferedReader(InputStreamReader(socket.getInputStream()))
rMessage = input!!.readLine()
println(rMessage)
while(rMessage != ""){
output!!.write("hello from client" + "\n")
output!!.flush()
rMessage = input!!.readLine()
}
output!!.close();
input!!.close();
socket.close();
} catch (e: Exception) {
e.printStackTrace()
}
}
}

#saulyasar -
If I gave you an example, it would be in Java, not Kotlin ;)
Stylistically, you should prefer "use()" to explicit "close()". It won't help your immediate problem - but it's a good habit :)
Your problem is: while(receiveString != ""). You loop ... but "receiveString" is never modified. So the loop never terminates. Whoops!
SUGGESTED ALTERNATIVE:
Assignment not allowed in while expression?
BufferedReader(reader).use { r ->
r.lineSequence().forEach {
println(it)
}
}
I believe this idiom can be successfully applied to your socket "read" loop.
Please post back what you find :)

Related

suspend IO function never return

I have difficulties writing an UDP message receive loop for Android.
In the following code, in receiveLoop, the call to receiveMessages never returns and I therefore never enter the message treatment loop.
Note that I am still able to receive packets, but it stops when the channel buffer is full.
I would expect receiveMessages to return immediately, while the blocking IO loop inside it would still run forever.
class MySocketUDP(private val params: SocketParams) {
private val rcvSocket: DatagramSocket by lazy {
val sock = DatagramSocket(params.rcvPort)
sock.reuseAddress = true
sock.soTimeout = 1000
sock
}
suspend fun receiveMessages(channel: SendChannel<Message>) {
withContext(Dispatchers.IO) {
val buf = ByteArray(MAX_MSG_SIZE)
while (true) {
val pkt = DatagramPacket(buf, buf.size)
try {
if (channel.isClosedForSend) {
break
}
rcvSocket.receive(pkt)
val msg = packetToMessage(buf, 0, pkt.length)
Log.d("SOCKET", "filling channel with $msg")
channel.send(msg)
} catch (ex: SocketTimeoutException) {
} catch (ex: CancellationException) {
break
}
}
}
}
}
class MyModel {
private suspend fun receiveLoop(socket: MySocketUDP) {
withContext(Dispatchers.Main) {
val channel = Channel<Message>(16)
socket.receiveMessages(channel)
Log.d("MODEL", "Entering msg loop")
for (msg in channel) {
dispatchRcvMessage(msg)
}
}
}
}
Why does receiveMessages never return while it is running in the IO dispatcher and called from the Main dispatcher?
Do I need to actually spawn a thread to such producer/consumer work?
Can you show how to achieve such long blocking code nicely in a "coroutine-friendly" manner?
Thank you
receiveMessages() is a suspend function which calls another suspend function withContext(), which in turn has an infinite loop. So calling socket.receiveMessages(channel) will suspend code execution while the loop is not finished.
You need to launch separate coroutines for consumer and producer, e.g. using launch function.
Some example of using coroutines:
val someScope = CoroutineScope(Dispatchers.Main)
private suspend fun receiveLoop(socket: MySocketUDP) = someScope.launch {
val channel = Channel<Message>(16)
socket.receiveMessages(channel)
Log.d("MODEL", "Entering msg loop")
for (msg in channel) {
dispatchRcvMessage(msg)
}
}
// In MySocketUDP
suspend fun receiveMessages(channel: SendChannel<Message>) {
someAnotherScope.launch { // or can use coroutineScope builder function
val buf = ByteArray(MAX_MSG_SIZE)
while (true) {
val pkt = DatagramPacket(buf, buf.size)
try {
if (channel.isClosedForSend) {
break
}
rcvSocket.receive(pkt)
val msg = packetToMessage(buf, 0, pkt.length)
Log.d("SOCKET", "filling channel with $msg")
channel.send(msg)
} catch (ex: SocketTimeoutException) {
} catch (ex: CancellationException) {
break
}
}
}
}

No exception/error when no internet coroutine + retrofit

I have the following setup
Service
// ItunesService
suspend fun searchItunesPodcast(#Query("term") term: String): Response<PodcastResponse>
Repository
// ItunesRepo
override suspend fun searchByTerm(term: String) = withContext(ioDispatcher) {
return#withContext itunesService.searchItunesPodcast(term)
}
ViewModel
fun searchPodcasts(term: String) {
viewModelScope.launch {
_res.value = Result.loading()
try {
val response = itunesRepo.searchByTerm(term)
if (response.isSuccessful) { // Nothing from here when no internet
_res.value = Result.success(response.body())
} else {
_res.value = Result.error(response.errorBody().toString())
}
} catch (e: Exception) {
_res.value = Result.exception(e)
}
}
}
Everything works great until i turn off mobile data/internet on my testing device. _res value stuck on Loading state. I have tried adding break point at if (response.isSuccessful) when there is no internet and it seams like val response = itunesRepo.searchByTerm(term) never returns how can I fix this
I switched to using Flow api on my Repository
override suspend fun searchPodcasts(term: String) = flow {
emit(Result.Loading)
try {
val res = itunesService.searchItunesPodcast(term)
if (res.isSuccessful)
emit(Result.Success(res.body()))
else
emit(Result.Error("Generic error: ${res.code()}"))
} catch (e: Exception) {
emit(Result.Error("Unexpected error", e))
}
}.flowOn(ioDispatcher)
Then collect the results on my ViewModels

How to use socket in Android with Kotlin

I want to achieve something in Android using Kotlin to do:
If I click a button on the app, the app sends a word to a TCP server (which I wrote with python). The server will send back another word, and the app will show a toast message.
Here is what I have done so far, I can figure out the sending part but I can't manage to make it keep listening to the socket to hear from the server.
I am trying to use coroutine but after finding all the resources online, this is as best as I can get.
Also, I am not sure if I am setting the IP address in the correct manner.
Thank you in advance for your help!
'''
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val sendBtn = findViewById<Button>(R.id.sendBtn )
val ipBtn = findViewById<Button>(R.id.ipBtn)
val ipInput = findViewById<TextView>(R.id.ipInput)
var ipAddress: String = "192.168.0.101"
// Below is my attempt to keep listening to the socket, if commented, the sending would work.
// My guess is the IO thread is caught in the while loop so the other coroutines cannot use
// IO thread to send to the server.
CoroutineScope(IO).launch{
val socket = Socket(ipAddress, 9999)
var text = ""
while (true) {
text = BufferedReader(InputStreamReader(socket.inputStream)).readLine()
// if text is not null
// Toast.makeText(this#MainActivity, "Set IP", Toast.LENGTH_SHORT).show()
}
}
suspend fun sendMessage(message:String){
val socket = Socket(ipAddress, 9999)
socket.outputStream.write(message.toByteArray())
socket.close()
}
ipBtn.setOnClickListener {
Toast.makeText(this#MainActivity, "Set IP", Toast.LENGTH_SHORT).show()
ipAddress = ipInput.text.toString()
}
sendBtn .setOnClickListener {
CoroutineScope(IO).launch {
Log.d("TAG", "message")
sendMessage("record")
}
}
'''
To send a Data from P2P Fist we required a Server and Client . Here the Socket act as end point for sending and receiving data across the network .
Create a Server Class like this
class ServerClass() :Thread(){
lateinit var serverSocket:ServerSocket
lateinit var inputStream: InputStream
lateinit var outputStream: OutputStream
lateinit var socket: Socket
override fun run() {
try {
serverSocket = ServerSocket(8888)
socket = serverSocket.accept()
inputStream =socket.getInputStream()
outputStream = socket.getOutputStream()
}catch (ex:IOException){
ex.printStackTrace()
}
val executors = Executors.newSingleThreadExecutor()
val handler = Handler(Looper.getMainLooper())
executors.execute(Runnable{
kotlin.run {
val buffer = ByteArray(1024)
var byte:Int
while (true){
try {
byte = inputStream.read(buffer)
if(byte > 0){
var finalByte = byte
handler.post(Runnable{
kotlin.run {
var tmpMeassage = String(buffer,0,finalByte)
Log.i("Server class","$tmpMeassage")
}
})
}
}catch (ex:IOException){
ex.printStackTrace()
}
}
}
})
}
fun write(byteArray: ByteArray){
try {
Log.i("Server write","$byteArray sending")
outputStream.write(byteArray)
}catch (ex:IOException){
ex.printStackTrace()
}
}
}
Create a client Class where we need to pass hostaddress
class ClientClass(hostAddress: InetAddress): Thread() {
var hostAddress: String = hostAddress.hostAddress
lateinit var inputStream: InputStream
lateinit var outputStream: OutputStream
lateinit var socket: Socket
fun write(byteArray: ByteArray){
try {
outputStream.write(byteArray)
}catch (ex:IOException){
ex.printStackTrace()
}
}
override fun run() {
try {
socket = Socket()
socket.connect(InetSocketAddress(hostAddress,8888),500)
inputStream = socket.getInputStream()
outputStream = socket.getOutputStream()
}catch (ex:IOException){
ex.printStackTrace()
}
val executor = Executors.newSingleThreadExecutor()
var handler =Handler(Looper.getMainLooper())
executor.execute(kotlinx.coroutines.Runnable {
kotlin.run {
val buffer =ByteArray(1024)
var byte:Int
while (true){
try{
byte = inputStream.read(buffer)
if(byte>0){
val finalBytes = byte
handler.post(Runnable{
kotlin.run {
val tmpMeassage = String(buffer,0,finalBytes)
Log.i("client class", tmpMeassage)
}
})
}
}catch (ex:IOException){
ex.printStackTrace()
}
}
}
})
}
}
make sure server and client port should be same , this is two way communication where we can transfer data in both sides .

Ping website URL before loading it in WebView in Android Kotlin

Before loading a website in WebView, I want to check the URL and make sure that it loads. If it does, show the WebView; if not, show another View with a message.
The idea is to check if the website can be loaded and depending on the response show either the "Website cannot be loaded" screen or show the WebView with URL loaded.
I have already checked if the connection is available, so no need to worry about that.
Need to support API 25+.
My solution below is trying to do two things:
Using AsyncTask "ping" a website and then
By passing context from MainActivity, call a function to show WebView (using WeakReference here to achieve that)
class MainActivity : AppCompatActivity() {
private var websiteURL = "https://www.google.com"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// ...
webView.webViewClient = WebViewClient()
}
// I called this function after checking that there is Internet connection
private fun connectionResponseFunction(): String {
return if (isConnected) {
// *** is connected
// pass in "this" context from MainActivity
val downloadData = CheckLink(this)
downloadData.execute(websiteURL)
} else {
// *** not connected
}
}
private fun showWebView() {
webView.loadUrl(websiteURL)
}
companion object {
class CheckLink internal constructor(context: MainActivity) : AsyncTask<String, Void, String>() {
// Needed if you want to use webView or anything else from MainActivity
private val activityReference: WeakReference<MainActivity> = WeakReference(context)
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
val activity = activityReference.get()
if (activity == null || activity.isFinishing) return
if (result == "success") {
// URL loaded, show webView
activity.showWebView()
} else {
// URL didn't load
}
}
override fun doInBackground(vararg url: String?): String {
val linkLoaded = loadLink(url[0])
if (!linkLoaded) {
return "failure"
}
return "success"
}
private fun loadLink(urlPath: String?): Boolean {
try {
val url = URL(urlPath)
val connection: HttpURLConnection = url.openConnection() as HttpURLConnection
connection.setRequestProperty("Connection", "close")
connection.connectTimeout = 3000
connection.connect()
val response = connection.responseCode
// 200 for success
return if (response == 200) {
true
} else {
false
}
} catch (e: Exception) {
// Handle exceptions
when (e) {
is MalformedURLException -> "loadLink: Invalid URL ${e.message}"
is IOException -> "loadLink: IO Exception reading data: ${e.message}"
is SecurityException -> { e.printStackTrace()
"loadLink: Security Exception. Needs permission? ${e.message}"
}
else -> "Unknown error: ${e.message}"
}
}
return false // Error
}
}
}
}
I'm quite new to Android and Kotlin, so I'm open to any suggestions to make it better.
I could not find any recent code that works for API 25+.
Another (shorter) alternative to AsyncTask would it be using Thread:
Thread {
val result = isHostAvailable(BuildConfig.BASE_URL)
runOnUiThread {
if (result) {
// do something ...
}
else showToast("no connection to server")
}
}.start()
and
fun isHostAvailable(urlPath: String): Boolean {
try {
val url = URL(urlPath)
val connection: HttpURLConnection = url.openConnection() as HttpURLConnection
connection.setRequestProperty("Connection", "close")
connection.connectTimeout = 3000
connection.connect()
return when (connection.responseCode) {
200, 403 -> true
else -> false
}
} catch (e: Exception) {
when (e) {
is MalformedURLException -> "loadLink: Invalid URL ${e.message}"
is IOException -> "loadLink: IO Exception reading data: ${e.message}"
is SecurityException -> {
e.printStackTrace()
"loadLink: Security Exception. Needs permission? ${e.message}"
}
else -> "Unknown error: ${e.message}"
}
}
return false
}
Looks like ping doesn't work on emulators (How to Ping External IP from Java Android)...instead, try this on a real device, it'll work:
val process = Runtime.getRuntime().exec("/system/bin/ping -c 1 $SERVIDOR")
val pingResult = process .waitFor()
return pingResult == 0
By the way, the answer given by #quietbits is pretty obsolete...AsyncTask class was the way to go like 5 years ago...Since Android Studio supports kotlin, you should use coroutines!! the code line difference is huge...check this code labs for more info (https://codelabs.developers.google.com/codelabs/kotlin-coroutines/#0)

How to treat HttpURLConnection when failed to connect to URL

When my database is running, everything is okey. But when is not running my mobile app always crash.
Error message:
Caused by: java.net.ConnectException: Failed to connect to /httpURL.
How to fix problem?
here is my code:
AsyncTaskHandleJson().execute(url)
inner class AsyncTaskHandleJson : AsyncTask<String, String, String>() {
override fun doInBackground(vararg url: String?): String {
var text: String
var connection = URL(url[0]).openConnection() as HttpURLConnection
try {
connection.connect()
text = connection.inputStream.use { it.reader().use { reader -> reader.readText() } }
} finally {
connection.disconnect()
}
return text
}
override fun onPostExecute(result: String?) {
super.onPostExecute(result)
handleJson(result)
}
}
Since there is no catch block in your code, you are not catching any exceptions currently.
If you would like to handle the ConnectException, then you simply have to catch it:
override fun doInBackground(vararg url: String?): String {
var text = ""
var connection: HttpURLConnection? = null
try {
connection = URL(url[0]).openConnection() as HttpURLConnection
connection.connect()
text = connection.inputStream.use {
it.reader().use { reader ->
reader.readText()
}
}
} catch (ce: ConnectException) {
// ConnectionException occurred, do whatever you'd like
ce.printStackTrace()
} catch (e: Exception) {
// some other Exception occurred
e.printStackTrace()
} finally {
connection?.disconnect()
}
return text
}
Check out the Exceptions reference.

Categories

Resources