- 核心概念:理解 Socket 通信的基本原理。
- 实现步骤:分步讲解如何在 Android 中创建一个多线程的 Socket 服务器。
- 完整代码示例:提供可以直接运行的 Server 和 Client 代码。
- 关键注意事项:特别是 Android 的网络权限和主线程限制。
- 进阶话题:如使用 Service 运行服务器、处理心跳等。
核心概念:Socket 通信基础
想象一下打电话:

- 服务器:就像一个总机接线员,它一直在“监听”(Listen)一个特定的电话号码(IP 地址 + 端口),当有电话(客户端连接)打进来时,它接起电话,建立一条通话线路(Socket 连接)。
- 客户端:就像打电话的人,它知道总机的号码(服务器的 IP 和端口),主动拨打电话(发起连接)。
- Socket:一旦连接建立,Socket 就是那条通话线路,双方都可以通过这个线路发送和接收数据(OutputStream 和 InputStream)。
- 端口:一个设备可以有多个网络应用,端口就像不同的分机号,确保数据被送到正确的应用程序,端口号范围是 0-65535,0-1023 是系统保留端口,建议使用 1024 以上的端口。
重要流程:
- 服务器:创建一个
ServerSocket,绑定到一个端口,并调用accept()开始阻塞式监听。 - 客户端:创建一个
Socket,指定服务器的 IP 和端口,发起连接。 - 连接成功:
accept()方法返回一个新的Socket对象,代表与这个客户端的专用连接。 - 数据传输:服务器和客户端都通过各自的
Socket获取输入流和输出流来读写数据。 - 关闭连接:通信结束后,关闭所有的流和 Socket。
实现步骤:在 Android 上创建 Socket 服务器
在 Android 中实现 Socket 服务器,有几个必须注意的特殊点:
- 不能在主线程(UI 线程)进行网络操作:网络操作(如
accept(),read(),write())可能会阻塞线程,如果放在主线程,会导致应用 ANR (Application Not Responding),所有网络相关的代码都必须在 子线程 中执行。 - 需要网络权限:必须在
AndroidManifest.xml中声明INTERNET权限。
下面是详细的实现步骤:
步骤 1:添加网络权限
在 app/src/main/AndroidManifest.xml 文件中,在 <application> 标签之前添加以下权限:

<uses-permission android:name="android.permission.INTERNET" />
步骤 2:创建服务器线程类
为了不阻塞主线程,我们将所有服务器逻辑(ServerSocket 的创建、监听、数据处理)都放在一个独立的 Thread 或 Runnable 中。
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class SocketServerThread extends Thread {
private static final String TAG = "SocketServerThread";
private static final int SERVER_PORT = 8888; // 服务器端口号
// 使用一个集合来管理所有连接的客户端
private final List<ClientHandler> clients = new ArrayList<>();
private ServerSocket serverSocket;
private boolean isRunning = false;
@Override
public void run() {
super.run();
isRunning = true;
try {
// 1. 创建 ServerSocket 并绑定端口
serverSocket = new ServerSocket(SERVER_PORT);
Log.d(TAG, "Server is started, listening on port " + SERVER_PORT);
// 2. 循环监听客户端连接
while (isRunning) {
// accept() 是一个阻塞方法,会一直等待直到有客户端连接
Socket clientSocket = serverSocket.accept();
Log.d(TAG, "Client connected: " + clientSocket.getInetAddress().getHostAddress());
// 3. 为每个连接的客户端创建一个新的处理线程
ClientHandler clientHandler = new ClientHandler(clientSocket);
clients.add(clientHandler);
clientHandler.start();
}
} catch (IOException e) {
if (isRunning) {
Log.e(TAG, "Error creating server socket or accepting client connection", e);
}
} finally {
// 4. 关闭服务器
shutdown();
}
}
// 关闭服务器
public void shutdown() {
isRunning = false;
// 通知所有客户端服务器关闭
for (ClientHandler client : clients) {
client.close();
}
clients.clear();
if (serverSocket != null && !serverSocket.isClosed()) {
try {
serverSocket.close();
Log.d(TAG, "Server socket closed.");
} catch (IOException e) {
Log.e(TAG, "Error closing server socket", e);
}
}
}
// 内部类:处理单个客户端连接的线程
private class ClientHandler extends Thread {
private final Socket clientSocket;
private BufferedReader in;
private OutputStream out;
public ClientHandler(Socket socket) {
this.clientSocket = socket;
}
@Override
public void run() {
super.run();
try {
// 获取输入流,用于读取客户端发送的数据
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
// 获取输出流,用于向客户端发送数据
out = clientSocket.getOutputStream();
// 5. 循环读取客户端发送的消息
String line;
while (isRunning && (line = in.readLine()) != null) {
Log.d(TAG, "Received from client " + clientSocket.getInetAddress() + ": " + line);
// 示例:将收到的消息回显给客户端
sendMessage("Server Echo: " + line);
}
} catch (IOException e) {
Log.e(TAG, "Error in client handler", e);
} finally {
// 6. 客户端断开连接后,清理资源
close();
clients.remove(this);
Log.d(TAG, "Client disconnected: " + clientSocket.getInetAddress());
}
}
// 向当前客户端发送消息
public void sendMessage(String message) {
try {
if (out != null) {
out.write((message + "\n").getBytes()); // 注意:write 不会自动换行,通常我们手动添加
out.flush(); // 确保数据被立即发送
}
} catch (IOException e) {
Log.e(TAG, "Error sending message to client", e);
close(); // 发送失败,关闭连接
}
}
// 关闭与当前客户端的连接
public void close() {
try {
if (in != null) in.close();
if (out != null) out.close();
if (clientSocket != null && !clientSocket.isClosed()) {
clientSocket.close();
}
} catch (IOException e) {
Log.e(TAG, "Error closing client handler resources", e);
}
}
}
}
步骤 3:在 Activity 或 Service 中启动服务器
你会在一个 Activity 的 onStart() 或 onCreate() 方法中启动服务器线程,并在 onStop() 或 onDestroy() 中安全地关闭它。
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private SocketServerThread serverThread;
private TextView statusTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
statusTextView = findViewById(R.id.statusTextView);
Button startButton = findViewById(R.id.startButton);
Button stopButton = findViewById(R.id.stopButton);
startButton.setOnClickListener(v -> startServer());
stopButton.setOnClickListener(v -> stopServer());
// 获取本机IP地址,用于客户端连接
String ipAddress = getLocalIpAddress();
if (ipAddress != null) {
statusTextView.setText("Server Status: Not Started\nIP Address: " + ipAddress);
} else {
statusTextView.setText("Server Status: Not Started\nIP Address: Unknown");
Toast.makeText(this, "Cannot get IP address", Toast.LENGTH_SHORT).show();
}
}
private void startServer() {
if (serverThread == null || !serverThread.isAlive()) {
serverThread = new SocketServerThread();
serverThread.start();
statusTextView.setText("Server Status: Running\nIP Address: " + getLocalIpAddress());
Toast.makeText(this, "Server started", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Server is already running", Toast.LENGTH_SHORT).
