Server:8888 是Qt 写的Tcp 服务器 ip 是 192.168.2.103 port是8888
安卓手机运行 kotlin 语法的Tcp Client ,连接,收发数据
效果如下图
package com.example.myapplication
import android.os.Handler
import android.os.Looper
import android.util.Log
import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import java.net.Socket
class TcpClient(private val ipAddress: String, private val port: Int) {
private lateinit var socket: Socket
private lateinit var reader: BufferedReader
private lateinit var writer: BufferedWriter
// 接收线程
private val messageReceiverThread = Thread {
//主消息处理器,用于向外部发送tcp收到的数据
val handler = Handler(Looper.getMainLooper())
val buffer = StringBuilder()
val charBuffer = CharArray(1024) // 调整缓冲区大小
while (!Thread.currentThread().isInterrupted) {
try {
// val receivedData = reader.readLine() ?: ""
// Log.d("TcpClient",receivedData)
// handler.post {
// onDataReceived(receivedData)
// }
val bytesRead = reader.read(charBuffer)
if (bytesRead == -1) {
// 如果没有更多数据可读,则退出循环
Log.d("TcpClient","continue")
continue
}
// 清空缓冲区
buffer.clear()
// 将读取的数据追加到缓冲区
buffer.append(charBuffer, 0, bytesRead)
// 通知UI线程更新UI
handler.post {
Log.d("TcpClient","buffer : "+buffer.toString())
onDataReceived(buffer.toString())
}
} catch (e: Exception) {
Log.e("TcpClient","Exception")
e.printStackTrace()
break
}
}
}
// 外部调用,定义数据接收监听器接口
interface DataReceivedListener {
fun onDataReceived(data: String)
}
private var dataReceivedListener: DataReceivedListener? = null
// 外部调用,设置数据接收监听器
fun setDataReceivedListener(listener: DataReceivedListener) {
dataReceivedListener = listener
}
// 通知数据接收事件
private fun onDataReceived(data: String) {
dataReceivedListener?.onDataReceived(data)
}
// 1
fun connectToServer() {
try {
socket = Socket(ipAddress, port)
reader = BufferedReader(InputStreamReader(socket.getInputStream()))
writer = BufferedWriter(OutputStreamWriter(socket.getOutputStream()))
} catch (e: Exception) {
e.printStackTrace()
}
}
fun sendMessage(message: String) {
try {
writer.write(message)
// writer.newLine()
writer.flush()
} catch (e: Exception) {
e.printStackTrace()
}
}
// 2
fun startMessageReceiver() {
messageReceiverThread.start()
}
fun stopMessageReceiver() {
messageReceiverThread.interrupt()
}
fun close() {
try {
socket.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
package com.example.myapplication
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.Editable
import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
class MainActivity : AppCompatActivity() {
private lateinit var ipAddress: String
private var port: Int = 0
private lateinit var recvText: EditText
private lateinit var tcpClient: TcpClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recvText = findViewById(R.id.recvText)
// start tcp
val startButton: Button = findViewById(R.id.startBtn)
// 设置按钮点击事件
startButton.setOnClickListener {
// 设置IP地址和端口(请根据需要修改)
ipAddress = findViewById<EditText?>(R.id.ipText).text.toString()
port = findViewById<EditText?>(R.id.portNum).text.toString().toInt()
// 创建TcpClient实例
tcpClient = TcpClient(ipAddress, port)
// 设置数据接收监听器
tcpClient.setDataReceivedListener(object : TcpClient.DataReceivedListener {
override fun onDataReceived(data: String) {
// 在数据接收回调中更新UI
updateUI(data)
}
})
// 在新线程中执行连接操作
Thread {
tcpClient.connectToServer()
tcpClient.startMessageReceiver()
}.start()
}
// stop tcp
val stopBtn: Button = findViewById(R.id.stopBtn)
stopBtn.setOnClickListener {
Thread {
tcpClient.stopMessageReceiver()
tcpClient.close()
}.start()
}
// send on thread
val sendBtn: Button = findViewById(R.id.sendBtn)
sendBtn.setOnClickListener {
Thread {
var sendText : EditText= findViewById(R.id.sendText)
tcpClient.sendMessage(sendText.text.toString())
}.start()
}
var cleanBtn:Button = findViewById(R.id.cleanBtn)
cleanBtn.setOnClickListener {
recvText.text.clear()
}
}
private fun updateUI(data: String) {
Log.d("MainActivity","data:"+data)
val editableText = Editable.Factory.getInstance().newEditable(data)
recvText.text?.append(editableText)
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="406dp"
android:layout_height="53dp"
android:orientation="horizontal">
<EditText
android:id="@+id/ipText"
android:layout_width="204dp"
android:layout_height="47dp"
android:layout_weight="4"
android:ems="10"
android:inputType="text"
android:text="192.168.2.103" />
<EditText
android:id="@+id/portNum"
android:layout_width="204dp"
android:layout_height="45dp"
android:layout_weight="1"
android:ems="10"
android:inputType="number"
android:text="8888" />
LinearLayout>
<LinearLayout
android:layout_width="409dp"
android:layout_height="55dp"
android:orientation="horizontal">
<Button
android:id="@+id/startBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="start" />
<Button
android:id="@+id/stopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="stop" />
<Space
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />
LinearLayout>
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="send data"
android:textSize="20sp" />
<EditText
android:id="@+id/sendText"
android:layout_width="match_parent"
android:layout_height="148dp"
android:layout_weight="4"
android:ems="10"
android:gravity="start|top"
android:inputType="textMultiLine" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/sendBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="send" />
<Button
android:id="@+id/cleanBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="clean recv" />
LinearLayout>
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="recv data"
android:textSize="20sp" />
<EditText
android:id="@+id/recvText"
android:layout_width="match_parent"
android:layout_height="202dp"
android:layout_weight="4"
android:ems="10"
android:gravity="start|top"
android:inputType="textMultiLine" />
LinearLayout>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapplication">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication">
<activity
android:name=".MainActivity"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
activity>
application>
manifest>
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.example.myapplication"
minSdkVersion 28
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
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
使用国内镜像
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/jcenter' }
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
// google()
// mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:4.2.1"
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/jcenter' }
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
// google()
// mavenCentral()
// jcenter() // Warning: this repository is going to shut down soon
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}