凌峰创科服务平台

Android Socket 服务器如何高效稳定运行?

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

核心概念:Socket 通信基础

想象一下打电话:

Android Socket 服务器如何高效稳定运行?-图1
(图片来源网络,侵删)
  • 服务器:就像一个总机接线员,它一直在“监听”(Listen)一个特定的电话号码(IP 地址 + 端口),当有电话(客户端连接)打进来时,它接起电话,建立一条通话线路(Socket 连接)。
  • 客户端:就像打电话的人,它知道总机的号码(服务器的 IP 和端口),主动拨打电话(发起连接)。
  • Socket:一旦连接建立,Socket 就是那条通话线路,双方都可以通过这个线路发送和接收数据(OutputStream 和 InputStream)。
  • 端口:一个设备可以有多个网络应用,端口就像不同的分机号,确保数据被送到正确的应用程序,端口号范围是 0-65535,0-1023 是系统保留端口,建议使用 1024 以上的端口。

重要流程:

  1. 服务器:创建一个 ServerSocket,绑定到一个端口,并调用 accept() 开始阻塞式监听。
  2. 客户端:创建一个 Socket,指定服务器的 IP 和端口,发起连接。
  3. 连接成功accept() 方法返回一个新的 Socket 对象,代表与这个客户端的专用连接。
  4. 数据传输:服务器和客户端都通过各自的 Socket 获取输入流和输出流来读写数据。
  5. 关闭连接:通信结束后,关闭所有的流和 Socket。

实现步骤:在 Android 上创建 Socket 服务器

在 Android 中实现 Socket 服务器,有几个必须注意的特殊点:

  • 不能在主线程(UI 线程)进行网络操作:网络操作(如 accept(), read(), write())可能会阻塞线程,如果放在主线程,会导致应用 ANR (Application Not Responding),所有网络相关的代码都必须在 子线程 中执行。
  • 需要网络权限:必须在 AndroidManifest.xml 中声明 INTERNET 权限。

下面是详细的实现步骤:

步骤 1:添加网络权限

app/src/main/AndroidManifest.xml 文件中,在 <application> 标签之前添加以下权限:

Android Socket 服务器如何高效稳定运行?-图2
(图片来源网络,侵删)
<uses-permission android:name="android.permission.INTERNET" />

步骤 2:创建服务器线程类

为了不阻塞主线程,我们将所有服务器逻辑(ServerSocket 的创建、监听、数据处理)都放在一个独立的 ThreadRunnable 中。

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 中启动服务器

你会在一个 ActivityonStart()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).
Android Socket 服务器如何高效稳定运行?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇