【发布时间】:2021-08-02 19:51:30
【问题描述】:
我遇到了 UDP 数据报的问题,因为我无法从服务器接收 UDP 数据包,但我可以发送它们。我查看了许多示例,但无法弄清楚我的代码有什么问题。我终于从不同的网站找到了问题所在的提示。
因此,我在此处更新了问题,以防将来可能对某人有所帮助。下面的代码在 LG 手机上通过 WiFi 网络运行,并且基于 Android Studio 4.2 (29/4/2021); SDK平台30; Kotlin 1.5.0
在下面代码部分的末尾,我写了一些关于是什么导致我的代码无法工作的 cmets。
这是我的 MainActivity 代码
//Required includes
import android.os.Bundle
import android.os.StrictMode
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import java.io.IOException
import java.net.*
class MainActivity : AppCompatActivity() {
//declared variables
private var clientThread: ClientThread? = null
private var thread: Thread? = null
private var tv:TextView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_new)
//Create a thread so that the received data does not run within the main user interface
clientThread = ClientThread()
thread = Thread(clientThread)
thread!!.start()
// create a value that is linked to a button called (id) MyButton in the layout
val buttonPress = findViewById<Button>(R.id.MyButton)
tv = findViewById(R.id.rcvdData)
tv!!.text = "Data Captured"
//Create a listener that will respond if MyButton is clicked
buttonPress.setOnClickListener{
//send a UDP package as a test
sendUDP("Hello")
}
}
//************************************ Some test code to send a UDP package
fun sendUDP(messageStr: String) {
// Hack Prevent crash (sending should be done using a separate thread)
val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()
StrictMode.setThreadPolicy(policy) //Just for testing relax the rules...
try {
//Open a port to send a UDP package
val socket = DatagramSocket()
socket.broadcast = true
val sendData = messageStr.toByteArray()
val sendPacket = DatagramPacket(sendData, sendData.size, InetAddress.getByName(SERVER_IP), SERVERPORT)
socket.send(sendPacket)
println("Packet Sent")
} catch (e: IOException) {
println(">>>>>>>>>>>>> IOException "+e.message)
}
}
//************************************* Some test code for receiving a UDP package
internal inner class ClientThread : Runnable {
private var socket: DatagramSocket? = null
private val recvBuf = ByteArray(1500)
private val packet = DatagramPacket(recvBuf, recvBuf.size)
// **********************************************************************************************
// * Open the network socket connection and start receiving a Byte Array *
// **********************************************************************************************
override fun run() {
try {
//Keep a socket open to listen to all the UDP trafic that is destined for this port
socket = DatagramSocket(CLIENTPORT)
while (true) {
//Receive a packet
socket!!.receive(packet)
//Packet received
println("Packet received from: " + packet.address.hostAddress)
val data = String(packet.data).trim { it <= ' ' }
println("Packet received; data: $data")
//Change the text on the main activity view
runOnUiThread { tv?.text = data }
}
}
catch (e1: IOException) {
println(">>>>>>>>>>>>> IOException "+e1.message)
socket?.close()
}
catch (e2: UnknownHostException) {
println(">>>>>>>>>>>>> UnknownHostException "+e2.message)
socket?.close()
}
finally{
socket?.close()
}
}
}
companion object {
val CLIENTPORT = 3000
val SERVERPORT = 3000
val SERVER_IP = "192.168.8.102"
}
}
在我的清单文件中,我添加了这个权限
<uses-permission android:name="android.permission.INTERNET"/>
“activity_new.xml”仅包含一个 ID 为 MyButton 的按钮和一个 ID 为 rcvdData 的 TextView
gradle.build(项目)
buildscript {
ext.kotlin_version = "1.5.0"
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
gradle.build(模块)
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.example.udptry1"
minSdkVersion 23
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
虽然我可以发送数据报 UDP 数据包,但我无法接收它们。在终于正确接收 UDP 数据包后,我只能接收 UNICAST 消息(仅针对我的 IP 的消息)而不是 BROADCAST 消息(针对多个设备的消息)。
我无法接收消息的原因是我在 PC 上模拟接收器。 Android 的模拟器会更改被模拟设备的 IP,并且不会将其绑定到 PC 的 IP 地址。这意味着虽然我的电脑会收到广播消息,但模拟的手机却没有。有关更多详细信息,请查看此链接 (https://developer.android.com/studio/run/emulator-networking)
我只能接收 UNCAST 而不能接收 BROADCAST 消息的原因是,一旦我的手机上的代码可以工作,我会在编码时离开手机。这意味着手机的屏幕将进入睡眠状态。显然有大量的手机会在手机进入睡眠状态时禁用监听广播消息以节省电力。一旦手机处于唤醒状态,它就会收听广播消息。
获取多播锁定似乎不会影响我手机上的此功能(我尝试这样做只是为了进行广播,所以很遗憾,如果您实际上使用的是多播套接字而不是数据报套接字,我不知道它是否可以工作)
【问题讨论】:
-
您确定接收方先于发送方启动吗?为什么不直播的时候设置
broadcast = true? -
感谢您的 cmets。我找到了答案并将其发布在问题中,以防万一它可以帮助其他人!
标签: android networking udp broadcast datagram