凌峰创科服务平台

Android socket服务器端如何实现高效通信?

这是一个非常经典但也很重要的话题,需要注意的是,Android 系统对后台网络服务有严格的限制,直接在主线程上运行 Socket 服务器是行不通的,并且即使放在后台,也容易被系统杀死,我们通常会结合前台服务来保证其稳定性。

Android socket服务器端如何实现高效通信?-图1
(图片来源网络,侵删)

下面我将提供一个完整的、可运行的示例,并解释其中的关键点和最佳实践。


核心概念

  1. Socket (套接字):是网络通信的端点,服务器端通过 ServerSocket 在一个特定端口上“监听”客户端的连接请求,一旦有客户端连接,服务器就会创建一个新的 Socket 与该客户端进行一对一的通信。
  2. 线程:网络操作(如 accept(), read(), write())是阻塞的,如果这些操作在主线程(UI 线程)上执行,会导致应用界面卡死甚至崩溃,我们必须将这些操作放在子线程中。
  3. 前台服务:这是 Android 推荐的用于运行长时间运行的后台任务的服务,它会显示一个持续的通知,告诉用户这个应用正在执行一个重要的任务,从而降低了系统杀死它的概率,对于 Socket 这是一个很好的选择。
  4. 线程池:服务器需要能够同时处理多个客户端的连接,为每个客户端创建一个新线程是一种简单的方式,但如果客户端数量非常多,会导致资源耗尽,使用线程池(如 ExecutorService)可以更高效地管理线程。

完整示例代码

我们将创建一个应用,它启动一个前台服务,该服务内部运行一个 Socket 服务器,服务器可以接收客户端发来的消息,并将收到的消息通过广播发送给 Activity,以便在 UI 上显示。

添加网络权限

AndroidManifest.xml 中,必须声明网络权限。

<!-- AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.socketserver">
    <!-- 网络权限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- 如果需要使用网络,Android 9 (API 28) 及以上版本还需要此权限 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <application ...>
        ...
    </application>
</manifest>

创建布局文件

用于显示服务器状态和接收到的消息。

Android socket服务器端如何实现高效通信?-图2
(图片来源网络,侵删)
<!-- activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">
    <TextView
        android:id="@+id/tv_status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="服务器状态: 未启动"
        android:textSize="18sp"
        android:layout_marginBottom="16dp"/>
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
        <TextView
            android:id="@+id/tv_messages"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="等待接收消息..."
            android:textSize="16sp" />
    </ScrollView>
    <Button
        android:id="@+id/btn_start_server"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="启动服务器" />
    <Button
        android:id="@+id/btn_stop_server"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="停止服务器" />
</LinearLayout>

创建广播接收器

用于从服务接收消息并更新 UI。

// MessageReceiver.java
package com.example.socketserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.TextView;
public class MessageReceiver extends BroadcastReceiver {
    private TextView tvMessages;
    public MessageReceiver(TextView tvMessages) {
        this.tvMessages = tvMessages;
    }
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent != null && intent.getAction().equals(SocketService.ACTION_CLIENT_MESSAGE)) {
            String message = intent.getStringExtra(SocketService.EXTRA_MESSAGE);
            // 在UI线程上更新TextView
            tvMessages.append("\n客户端: " + message);
        }
    }
}

创建 Socket 服务

这是核心部分,包含了服务器逻辑。

// SocketService.java
package com.example.socketserver;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Enumeration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SocketService extends Service {
    private static final String TAG = "SocketService";
    private static final int PORT = 8888;
    private static final String CHANNEL_ID = "SocketServiceChannel";
    public static final String ACTION_CLIENT_MESSAGE = "com.example.socketserver.CLIENT_MESSAGE";
    public static final String EXTRA_MESSAGE = "message";
    private ServerSocket serverSocket;
    private ExecutorService executorService;
    private boolean isRunning = false;
    @Override
    public void onCreate() {
        super.onCreate();
        createNotificationChannel();
        executorService = Executors.newCachedThreadPool();
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        startForeground(1, createNotification());
        startServer();
        return START_STICKY; // 如果服务被杀死,系统会尝试重新创建它
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null; // 我们不使用绑定服务
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        stopServer();
    }
    private void startServer() {
        if (isRunning) return;
        isRunning = true;
        executorService.execute(() -> {
            try {
                // 获取本机IP地址,方便客户端连接
                String ipAddress = getLocalIpAddress();
                Log.d(TAG, "服务器启动中... IP: " + ipAddress + ", 端口: " + PORT);
                serverSocket = new ServerSocket(PORT);
                Log.d(TAG, "服务器已启动,等待客户端连接...");
                // 持续监听客户端连接
                while (isRunning) {
                    try {
                        Socket clientSocket = serverSocket.accept();
                        Log.d(TAG, "客户端已连接: " + clientSocket.getInetAddress());
                        // 为每个客户端创建一个处理任务
                        ClientHandler clientHandler = new ClientHandler(clientSocket);
                        executorService.execute(clientHandler);
                    } catch (IOException e) {
                        if (isRunning) {
                            Log.e(TAG, "接受客户端连接时出错", e);
                        }
                    }
                }
            } catch (IOException e) {
                Log.e(TAG, "启动服务器时出错", e);
            }
        });
    }
    private void stopServer() {
        isRunning = false;
        if (serverSocket != null && !serverSocket.isClosed()) {
            try {
                serverSocket.close();
                Log.d(TAG, "服务器已停止");
            } catch (IOException e) {
                Log.e(TAG, "关闭服务器时出错", e);
            }
        }
        if (executorService != null) {
            executorService.shutdown();
        }
        stopForeground(true);
        stopSelf();
    }
    // 处理单个客户端连接的内部类
    private class ClientHandler implements Runnable {
        private final Socket clientSocket;
        public ClientHandler(Socket socket) {
            this.clientSocket = socket;
        }
        @Override
        public void run() {
            try (
                BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)
            ) {
                String inputLine;
                while (isRunning && clientSocket.isConnected()) {
                    inputLine = in.readLine();
                    if (inputLine == null) {
                        break; // 客户端断开连接
                    }
                    Log.d(TAG, "收到消息: " + inputLine);
                    // 通过广播将消息发送给Activity
                    Intent intent = new Intent(ACTION_CLIENT_MESSAGE);
                    intent.putExtra(EXTRA_MESSAGE, inputLine);
                    sendBroadcast(intent);
                    // 简单的响应
                    out.println("服务器收到: " + inputLine);
                }
            } catch (IOException e) {
                Log.e(TAG, "处理客户端时出错", e);
            } finally {
                try {
                    clientSocket.close();
                } catch (IOException e) {
                    Log.e(TAG, "关闭客户端Socket时出错", e);
                }
                Log.d(TAG, "客户端连接已关闭");
            }
        }
    }
    // 获取本机IP地址
    private String getLocalIpAddress() {
        try {
            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces
Android socket服务器端如何实现高效通信?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇