102
社区成员
发帖
与我相关
我的任务
分享Socket通信是一种网络通信方式,它允许两台主机或设备之间进行双向的数据交换。具体来说,Socket可以看作是网络通信过程中端点的抽象表示。
Socket的主要特点包括:
通信协议:Socket可以基于不同的通信协议进行数据传输,例如TCP/IP、UDP等。
通信模型:Socket可以支持不同的通信模型,如面向连接的流式Socket(如TCP)和无连接的数据报式Socket(如UDP)。
客户端-服务器模型:Socket通常用于实现客户端-服务器模型,其中客户端应用程序通过Socket与服务器进行通信。
端口号:Socket通过端口号来标识应用程序的通信端口,以便不同的应用程序能够同时在同一台主机上进行网络通信。
Socket通信原理主要涉及客户端和服务端两个方面。首先,在服务器端创建一个服务器套接字(ServerSocket),并把它附加到一个端口上,服务器从这个端口监听连接。然后,客户端创建一个Socket对象,指定要连接的服务器和端口号,向服务器发送连接请求。服务器收到连接请求后,创建一个Socket对象与客户端进行通信。
2.1.1创建一个新的Android项目,命名为Server并添加INTERNET权限
<uses-permission android:name="android.permission.INTERNET" />
使用下列Android UI 元素:TextView:用于显示从用户端接收到的消息;EditText:用于输入发送给用户端文本消息的文本框;Button:用于发送消息的按钮;
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView2307"
android:layout_width="300dp"
android:layout_height="100dp"
android:textSize="30sp" />
<EditText
android:id="@+id/editText"
android:layout_width="300dp"
android:layout_height="100dp"/>
<Button
android:id="@+id/sendButton"
android:layout_width="300dp"
android:layout_height="100dp"
android:layout_marginTop="20dp"
android:text="from Server to Client"/>
</LinearLayout>
使用acceptClients()它用于在服务器端启动一个线程,用于接受客户端的连接,然后创建一个 ServerSocket 对象,用于监听指定端口上的客户端连接。再通过一个 while 循环来多次接受客户端的连接,直到服务器关闭。最后,在 finally 块中关闭了服务器套接字,确保在服务器关闭时资源得到正确释放。handleClient函数首先获取客户端的输入流和输出流,用于接收客户端发送的消息和向客户端发送响应消息,然后通过一个 while 循环来持续读取客户端发送的消息,直到客户端关闭连接或发送了终止消息。如果需要向客户端发送响应消息,则会等待 hint 标志的改变,一旦有新的消息需要发送,则将消息写入输出流。最后,在 finally 块中关闭了客户端套接字。
fun acceptClients(messageListener: (String) -> Unit) {
Thread {
try {
serverSocket = ServerSocket(port)
isServerRunning = true
while (isServerRunning) {
val clientSocket = serverSocket!!.accept()
handleClient(clientSocket, messageListener)
}
outputStream.flush()
} catch (e: IOException) {
e.printStackTrace()
} finally {
// 关闭服务器套接字
serverSocket?.close()
}
}.start()
}
// 处理客户端连接
private fun handleClient(clientSocket: Socket, messageListener: (String) -> Unit) {
Thread {
try {
val inputStream: InputStream = clientSocket.getInputStream()
val outputStream: OutputStream = clientSocket.getOutputStream()
val bufferedReader = BufferedReader(InputStreamReader(inputStream))
val printWriter = PrintWriter(outputStream, true)
var inputLine: String?
while (bufferedReader.readLine().also { inputLine = it } != null) { // 读取客户端消息
messageListener.invoke(inputLine ?: "")
var response: String
while (hint == 0) {
Thread.sleep(3000)
}
response = ServerMessage.toString()
hint = 0
printWriter.println(response)
}
} catch (e: IOException) {
e.printStackTrace()
} finally {
// 关闭客户端套接字
clientSocket.close()
}
}.start()
}
package com.example.server
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
private val server = Server(2307)
private lateinit var editText: EditText
private lateinit var sendButton: Button
private lateinit var textView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initViewReferences()
setupServer()
setupSendButton()
}
private fun initViewReferences() {
editText = findViewById(R.id.editText)
sendButton = findViewById(R.id.sendButton)
textView = findViewById(R.id.textView2307)
}
private fun setupServer() {
server.acceptClients { receivedMessage ->
runOnUiThread {
textView.text = receivedMessage
}
}
}
private fun setupSendButton() {
sendButton.setOnClickListener {
val message = editText.text.toString()
if (message.isNotEmpty()) {
server.sendMessage(message)
}
}
}
override fun onDestroy() {
super.onDestroy()
server.stopServer()
}
}
布局文件与服务器端相同,包含四个 Android UI 元素
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/Message"
android:layout_width="300dp"
android:layout_height="110dp"
android:textSize="20sp"
android:textAlignment="center"
android:layout_below="@id/connect"
/>
<Button
android:id="@+id/sendMessage"
android:layout_width="300dp"
android:layout_height="110dp"
android:textAlignment="center"
android:layout_below="@id/message"
android:text="sendMessage"/>
<TextView
android:id="@+id/textViewExpression"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="25sp"
android:textAlignment="center"
android:text="2307Message from server"
android:padding="16dp" />
<Button
android:id="@+id/connect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="center"
android:layout_below="@id/textViewExpression"
android:text="Server Connect"/>
</LinearLayout>
客户端使用Client类,它用于创建一个客户端连接到指定 IP 和端口的服务器,定义了一个 Socket 属性,用于保存客户端与服务器的连接,连接到本地 IP 地址 127.0.0.1 和端口 2307并且使用线程来于监听从服务器发来的消息,并通过 messageListener 处理这些消息。sendMessage函数接受一个消息字符串作为参数,并将其发送给服务器。disconnect是一个用于断开与服务器连接的函数。它尝试关闭之前建立的 Socket 连接,并捕获可能的异常。在 disconnect 函数中,先检查 socket 是否已经被初始化,然后再尝试关闭连接。这是为了避免在未初始化的情况下关闭 Socket 引发异常。
package com.example.client
import java.io.*
import java.net.Socket
class Client {
private lateinit var socket: Socket
fun connectToServer(messageListener: (String) -> Unit) {
try {
socket = Socket("127.0.0.1", 2307)
val inputStream = socket.getInputStream()
val outputStream = socket.getOutputStream()
val bufferedReader = BufferedReader(InputStreamReader(inputStream))
PrintWriter(OutputStreamWriter(outputStream), true)
Thread {
var inputLine: String?
while (bufferedReader.readLine().also { inputLine = it } != null) {
messageListener.invoke(inputLine ?: "")
}
}.start()
} catch (e: Exception) {
e.printStackTrace()
}
}
fun sendMessage(message: String) {
try {
val printWriter = PrintWriter(socket.getOutputStream(), true)
printWriter.println(message)
} catch (e: Exception) {
e.printStackTrace()
}
}
fun disconnect() {
if (::socket.isInitialized) socket.close()
}
}
package com.example.client
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
private lateinit var client: Client
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
client = Client() // Initialize client
val connectButton = findViewById<Button>(R.id.connect)
val messageTextView = findViewById<TextView>(R.id.textViewExpression)
val sendBtn = findViewById<Button>(R.id.sendMessage)
val sendMessage = findViewById<EditText>(R.id.Message)
connectButton.setOnClickListener {
GlobalScope.launch(Dispatchers.IO) {
client.connectToServer { message ->
runOnUiThread {
messageTextView.text = message
}
}
}
}
sendBtn.setOnClickListener {
GlobalScope.launch(Dispatchers.IO) {
val message: String = sendMessage.text.toString()
client.sendMessage(message)
}
}
}
override fun onDestroy() {
super.onDestroy()
client.disconnect()
}
}
1:对client报错
解决方案:配置参数出现错误,在android studio中已有默认参数,不需要再自行添加。
本次实验对对于两个app实现socket通信进行了实践,考察了socket编程相关的知识,由于我对相关知识了解较少,本节课中完成实验借鉴了很多网络上已有的经验。这让我体会到搜索与应用能力在开发活动中同样十分重要。在日后的学习中我也要加强自己这方面的能力,勇于实践不熟悉的方向,从而解决难题。