参考Android开发艺术探索
Socket
Socket:套接字,网络通信值的概念,分为流式套接字和用户数据套接字两种。
-
TCP
流式套接字, 面向连接的协议,提供稳定的双向通信功能,TCP的连接的建立要通过“三次握手”才能完成,为了提供稳定的数据传输功能,其本身也提供了超时重传的机制,因此具有很高的稳定性。
-
UDP
UDP是无连接的,提供不稳定的单向通信功能,但让UDP也可以实现双向通信功能。在性能上,UDP具有更好的效率,其缺点是不能保证数据一定能够正确传输,尤其是网络拥塞的情况下。
代码实现
权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
服务端
class TCPServerService : Service() {
private var mIsServiceDestroyed = false
private val mDefinedMessages = arrayOf("你好啊,洒洒水所", "今天上海听爱情", "我屮艸芔茻", "你这人真尼玛操蛋", "j今Tina踩了一坨屎")
private val mTCPServerThread = Thread(Runnable {
//监听本地8688 端口
val serverSocket = ServerSocket(8688)
//循环接收客户端的连接请求,用来支持多个客户端连接
while (!mIsServiceDestroyed) {
val client = serverSocket.accept()
println("accept")
//每接收到一个客户端的连接请求之后,开启一个线程用来处理客户端的消息
Thread(Runnable { responseClient(client) }).start()
}
})
override fun onCreate() {
super.onCreate()
mTCPServerThread.start()
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onDestroy() {
mIsServiceDestroyed = true
super.onDestroy()
}
/**
* 接收客户端的消息,并向客户端灰回复消息
*/
private fun responseClient(client: Socket) {
val reader = BufferedReader(InputStreamReader(client.getInputStream()))
val writer = PrintWriter(BufferedWriter(OutputStreamWriter(client.getOutputStream())), true)
writer.println("欢迎来到聊天室!")
//循环接收当前线程对应的客户端发来消息,并回复
while (!mIsServiceDestroyed) {
val str: String? = reader.readLine()
println("msg from client : $str")
if (str == null) {
break
}
val i = Random().nextInt(mDefinedMessages.size)
writer.println(mDefinedMessages[i])
println(mDefinedMessages[i])
}
//循环结束后,关闭操作
println("client quit.")
reader.close()
writer.close()
client.close()
}
}
客户端
class TCPClientActivity : AppCompatActivity() {
companion object {
private const val MSG_RECEIVE_NEW_MSG = 1
private const val MSG_SOCKET_CONNECTED = 2
}
private var mClientSocket: Socket? = null
private var mPrintWriter: PrintWriter? = null
@SuppressLint("SetTextI18n")
private val mHandler = Handler(Handler.Callback {
when (it.what) {
MSG_RECEIVE_NEW_MSG -> tvMsgContainer.text = tvMsgContainer.text.toString() + it.obj
MSG_SOCKET_CONNECTED -> btnSend.isEnabled = true
}
true
})
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_tcpclient)
btnSend.setOnClickListener { clickBtnSend() }
startService(Intent(act, TCPServerService::class.java))
Thread(Runnable { connectTCPServer() }).start()
// btnSend.postDelayed({ }, 3000)//延迟连接,等待服务端启动完成
}
private fun connectTCPServer() {
var socket: Socket? = null
while (socket == null) {
//通过循环来处理服务端启动延迟的问题,当然这里的循环也是基于处理失败重连的机制上的
//如果不做try catch 处理,当连接失败时会抛错
try {
socket = Socket("localhost", 8688)
mClientSocket = socket
mPrintWriter = PrintWriter(BufferedWriter(OutputStreamWriter(socket.getOutputStream())), true)
mHandler.sendEmptyMessage(MSG_SOCKET_CONNECTED)
println("connect server success")
} catch (e: Exception) {
SystemClock.sleep(1000)
println("connect server failed, retry...")
}
}
val br = BufferedReader(InputStreamReader(socket.getInputStream()))
while (!isFinishing) {
var msg: String? = null
try {
msg = br.readLine()
} catch (e: Exception) {
e.printStackTrace()
}
if (msg != null) {
val time = formatDateTime(System.currentTimeMillis())
val showedMsg = "server $time: $msg\n"
mHandler.obtainMessage(MSG_RECEIVE_NEW_MSG, showedMsg).sendToTarget()
}
}
println("quit...")
mPrintWriter?.close()
br.close()
socket.close()
}
private fun formatDateTime(time: Long): String {
return SimpleDateFormat("(HH:mm:ss)", Locale.CHINA).format(Date(time))
}
@SuppressLint("SetTextI18n")
private fun clickBtnSend() {
val msg: String? = inputMsg.text.toString()
if (!msg.isNullOrEmpty()) {
mPrintWriter?.println(msg)
inputMsg.setText("")
val time = formatDateTime(System.currentTimeMillis())
val showedMsg = "self $time : $msg\n"
tvMsgContainer.text = tvMsgContainer.text.toString() + showedMsg
}
}
override fun onDestroy() {
mClientSocket?.shutdownInput()
mClientSocket?.close()
mHandler.removeCallbacksAndMessages(null)
super.onDestroy()
}
}
AndroidManifest
<service
android:name=".demo.socket.TCPServerService"
android:process=":tcp_server" />
<activity
android:name=".demo.socket.TCPClientActivity"
android:windowSoftInputMode="adjustResize" />
网友评论